Refactor to solve circular references

This commit is contained in:
Fergal Moran
2024-03-22 19:34:30 +00:00
parent 1e5ab38413
commit 8da0d43f23
110 changed files with 1607 additions and 932 deletions

View File

@@ -2,14 +2,14 @@ using System.Security.Claims;
using Humanizer;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Data.Models.Medias;
using TvNoms.Server.Services.Data.Models.Users;
using TvNoms.Server.Services.Data.Models.Users.Accounts;
using TvNoms.Server.Services.Data.Services;
using TvNoms.Server.Services.Identity;
using TvNoms.Server.Services.Utilities;
using TvNoms.Server.Services.Validation.Exceptions;
using TvNoms.Core.Entities;
using TvNoms.Core.Extensions.Identity;
using TvNoms.Core.Models.Medias;
using TvNoms.Core.Models.Users;
using TvNoms.Core.Models.Users.Accounts;
using TvNoms.Core.Utilities;
using TvNoms.Core.Utilities.Validation.Exceptions;
using TvNoms.Server.Services;
namespace TvNoms.Server.ApiService.Endpoints;

View File

@@ -5,18 +5,18 @@ using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using server.ServiceDefaults;
using TvNoms.Core.Entities;
using TvNoms.Core.Models;
using TvNoms.Core.Utilities;
using TvNoms.Infrastructure.Identity;
using TvNoms.Infrastructure.Messaging.Email;
using TvNoms.Infrastructure.Messaging.SMS;
using TvNoms.Infrastructure.Storage;
using TvNoms.Infrastructure.ViewRenderer.Razor;
using TvNoms.Server.ApiService.Shared;
using TvNoms.Server.Data;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Data.Models;
using TvNoms.Server.Services.Data.Repositories;
using TvNoms.Server.Services.Identity;
using TvNoms.Server.Services.Infrastructure.Messaging;
using TvNoms.Server.Services.Infrastructure.Messaging.Email;
using TvNoms.Server.Services.Infrastructure.Messaging.SMS;
using TvNoms.Server.Services.Infrastructure.Storage;
using TvNoms.Server.Services.Utilities;
using TvNoms.Server.Services.ViewRenderer.Razor;
using TvNoms.Server.Data.Repositories;
var builder = WebApplication.CreateBuilder(args);
var assemblies = AssemblyHelper.GetAssemblies().ToArray();

View File

@@ -1,4 +1,4 @@
using TvNoms.Server.Services.Utilities;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.ApiService.Shared;

View File

@@ -9,6 +9,8 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\TvNoms.Data\TvNoms.Data.csproj" />
<ProjectReference Include="..\TvNoms.Infrastructure\TvNoms.Infrastructure.csproj" />
<ProjectReference Include="..\TvNoms.ServiceDefaults\TvNoms.ServiceDefaults.csproj" />
<ProjectReference Include="..\TvNoms.Services\TvNoms.Services.csproj" />
</ItemGroup>

View File

@@ -1,9 +1,6 @@
namespace TvNoms.Server.Data.Models;
public record Client : BaseEntity {
public Client() {
}
namespace TvNoms.Core.Entities;
public class Client : BaseEntity {
public string ConnectionId { get; set; } = default!;
public DateTimeOffset ConnectionTime { get; set; }

View File

@@ -1,15 +1,15 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TvNoms.Server.Data.Models;
namespace TvNoms.Core.Entities;
public interface IEntity {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid Id { get; }
}
public record BaseEntity : IEntity {
public class BaseEntity : IEntity {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]

View File

@@ -1,6 +1,6 @@
namespace TvNoms.Server.Data.Models;
namespace TvNoms.Core.Entities;
public record Media : BaseEntity {
public class Media : BaseEntity {
public string Name { get; set; } = default!;
public long Size { get; set; }

View File

@@ -0,0 +1,5 @@
namespace TvNoms.Core.Entities;
public class Movie() : BaseEntity {
public string Title { get; set; }
}

View File

@@ -0,0 +1,5 @@
namespace TvNoms.Core.Entities;
public class Show : BaseEntity {
public string Title { get; set; }
}

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Identity;
namespace TvNoms.Server.Data.Models;
namespace TvNoms.Core.Entities;
public class User : IdentityUser<Guid>, IEntity {
public string FirstName { get; set; }
@@ -20,10 +20,7 @@ public class User : IdentityUser<Guid>, IEntity {
}
public class UserRole : IdentityUserRole<Guid>, IEntity {
public Guid Id { get; set; }
public virtual User User { get; set; } = default!;
public virtual Role Role { get; set; } = default!;
Guid IEntity.Id { get; }
}
public class UserSession : IEntity {

View File

@@ -1,7 +1,7 @@
using MediatR;
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
namespace TvNoms.Server.Services.Events;
namespace TvNoms.Core.Events;
public class UserSignedIn : INotification {
public UserSignedIn(User user) {

View File

@@ -1,7 +1,7 @@
using MediatR;
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
namespace TvNoms.Server.Services.Events;
namespace TvNoms.Core.Events;
public class UserSignedOut : INotification {
public UserSignedOut(User user) {

View File

@@ -1,7 +1,7 @@
using MediatR;
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
namespace TvNoms.Server.Services.Events;
namespace TvNoms.Core.Events;
public class UserSignedUp : INotification {
public UserSignedUp(User user) {

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Infrastructure.Messaging.Email;
namespace TvNoms.Core.Extensions.EmailSender;
public class EmailAccount {
public string Username { get; set; } = default!;

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Infrastructure.Messaging.Email;
namespace TvNoms.Core.Extensions.EmailSender;
public class EmailAttachment {
public string FileName { get; set; } = default!;

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Infrastructure.Messaging.Email;
namespace TvNoms.Core.Extensions.EmailSender;
public class EmailMessage {
public string Subject { get; set; } = default!;

View File

@@ -0,0 +1,5 @@
namespace TvNoms.Core.Extensions.EmailSender;
public interface IEmailSender {
Task SendAsync(EmailMessage message, CancellationToken cancellationToken = default);
}

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Core.Extensions.Identity;
public interface IClientContext {
string? DeviceId { get; }

View File

@@ -1,6 +1,6 @@
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Core.Extensions.Identity;
public interface IUserSessionFactory {
Task<UserSessionInfo> GenerateAsync(User user, CancellationToken cancellationToken = default);

View File

@@ -3,7 +3,7 @@ using System.Security.Cryptography;
using System.Text;
using DeviceId;
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Core.Extensions.Identity;
public class TokenHelper {
public static string Secret => GenerateSHA256Hash(

View File

@@ -1,6 +1,6 @@
using System.Security.Claims;
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Core.Extensions.Identity;
public class UserSessionInfo {
public string AccessToken { get; set; } = default!;

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Core.Extensions.Identity;
public class UserSessionOptions {
public string Secret { set; get; } = default!;

View File

@@ -0,0 +1,5 @@
namespace TvNoms.Core.Extensions.SmsSender;
public interface ISmsSender {
Task SendAsync(string phoneNumber, string message, CancellationToken cancellationToken = default);
}

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.ViewRenderer.Razor;
namespace TvNoms.Core.Extensions.ViewRenderer;
public interface IViewRenderer {
Task<string> RenderAsync(string name, object? model = null, CancellationToken cancellationToken = default);

View File

@@ -1,7 +1,7 @@
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Utilities;
using TvNoms.Core.Entities;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Services.Infrastructure.Storage;
namespace TvNoms.Core.FileStorage;
public class FileRuleOptions {
public IList<FileRule> Documents { get; set; } = new List<FileRule>();

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Infrastructure.Storage;
namespace TvNoms.Core.FileStorage;
public interface IFileStorage {
Task WriteAsync(string path, Stream content, CancellationToken cancellationToken = default);

View File

@@ -1,6 +1,6 @@
using FluentValidation;
namespace TvNoms.Server.Services.Data.Models.Medias;
namespace TvNoms.Core.Models.Medias;
public class DeleteMediaForm {
public long Id { get; set; }

View File

@@ -1,7 +1,7 @@
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
using AbstractProfile = AutoMapper.Profile;
namespace TvNoms.Server.Services.Data.Models.Medias;
namespace TvNoms.Core.Models.Medias;
public class MediaModel {
public long Id { get; set; }

View File

@@ -1,7 +1,7 @@
using Microsoft.Extensions.Options;
using TvNoms.Server.Services.Infrastructure.Storage;
using TvNoms.Core.FileStorage;
namespace TvNoms.Server.Services.Data.Models.Medias;
namespace TvNoms.Core.Models.Medias;
public class UploadMediaChunkForm : UploadMediaContentForm {
public Guid Id { get; set; }

View File

@@ -1,11 +1,11 @@
using FluentValidation;
using Humanizer;
using Microsoft.Extensions.Options;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Infrastructure.Storage;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Entities;
using TvNoms.Core.FileStorage;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Data.Models.Medias;
namespace TvNoms.Core.Models.Medias;
public class UploadMediaContentForm {
public string Name { get; set; } = default!;

View File

@@ -1,13 +1,13 @@
using AutoMapper;
using Humanizer;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Data.Models.Users;
using TvNoms.Server.Services.Data.Repositories;
using TvNoms.Server.Services.Data.Utilities;
using TvNoms.Server.Services.Identity;
using TvNoms.Server.Services.Infrastructure.Storage;
using TvNoms.Core.Entities;
using TvNoms.Core.Extensions.Identity;
using TvNoms.Core.FileStorage;
using TvNoms.Core.Models.Users;
using TvNoms.Core.Repositories;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Services.Data.Models;
namespace TvNoms.Core.Models;
public interface IModelBuilder {
// User

View File

@@ -2,9 +2,9 @@
using FluentValidation;
using Humanizer;
using Microsoft.Extensions.DependencyInjection;
using TvNoms.Server.Services.Utilities;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Services.Data.Models;
namespace TvNoms.Core.Models;
public static class ServiceCollectionExtensions {
public static IServiceCollection AddModelBuilder(this IServiceCollection services) {

View File

@@ -1,7 +1,7 @@
using FluentValidation;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts;
namespace TvNoms.Core.Models.Users.Accounts;
public class ChangePasswordForm {
public string CurrentPassword { get; set; } = default!;

View File

@@ -1,6 +1,6 @@
using FluentValidation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts;
namespace TvNoms.Core.Models.Users.Accounts;
public class RefreshSessionForm {
public string RefreshToken { get; set; } = default!;

View File

@@ -1,8 +1,8 @@
using System.Text.Json.Serialization;
using FluentValidation;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts {
namespace TvNoms.Core.Models.Users.Accounts {
public class ResetPasswordForm {
public string Username { get; set; } = default!;

View File

@@ -1,8 +1,8 @@
using System.Text.Json.Serialization;
using FluentValidation;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts;
namespace TvNoms.Core.Models.Users.Accounts;
public class SendPasswordResetTokenForm {
public string Username { get; set; } = default!;

View File

@@ -1,8 +1,8 @@
using System.Text.Json.Serialization;
using FluentValidation;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts {
namespace TvNoms.Core.Models.Users.Accounts {
public class SendUsernameTokenForm {
public string Username { get; set; } = default!;

View File

@@ -1,8 +1,8 @@
using System.Text.Json.Serialization;
using FluentValidation;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts {
namespace TvNoms.Core.Models.Users.Accounts {
public class SignInForm {
public string Username { get; set; } = default!;

View File

@@ -1,6 +1,6 @@
using FluentValidation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts;
namespace TvNoms.Core.Models.Users.Accounts;
public class SignOutForm {
public string RefreshToken { get; set; } = default!;

View File

@@ -1,8 +1,8 @@
using System.Text.Json.Serialization;
using FluentValidation;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts {
namespace TvNoms.Core.Models.Users.Accounts {
public class SignUpForm {
public string FirstName { get; set; } = default!;

View File

@@ -1,8 +1,8 @@
using System.Text.Json.Serialization;
using FluentValidation;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts {
namespace TvNoms.Core.Models.Users.Accounts {
public class SignUpWithForm {
public string FirstName { get; set; } = default!;

View File

@@ -1,7 +1,7 @@
using FluentValidation;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Data.Models.Users.Accounts {
namespace TvNoms.Core.Models.Users.Accounts {
public class VerifyUsernameForm {
public string Username { get; set; } = default!;

View File

@@ -1,9 +1,9 @@
using FluentValidation;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Entities;
using TvNoms.Core.Utilities.Validation;
using AbstractProfile = AutoMapper.Profile;
namespace TvNoms.Server.Services.Data.Models.Users;
namespace TvNoms.Core.Models.Users;
public class EditUserForm {
public string UserName { get; set; } = default!;

View File

@@ -1,8 +1,8 @@
using System.Linq.Expressions;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Utilities;
using TvNoms.Core.Entities;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Services.Data.Models.Users;
namespace TvNoms.Core.Models.Users;
public class UserCriteria {
public Guid[]? Id { get; set; }

View File

@@ -1,7 +1,7 @@
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
using AbstractProfile = AutoMapper.Profile;
namespace TvNoms.Server.Services.Data.Models.Users;
namespace TvNoms.Core.Models.Users;
public class UserModel {
public long Id { get; set; }

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Data.Models.Users;
namespace TvNoms.Core.Models.Users;
public class UserPageModel {
public long Offset { get; set; }

View File

@@ -1,8 +1,8 @@
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Identity;
using TvNoms.Core.Entities;
using TvNoms.Core.Extensions.Identity;
using AbstractProfile = AutoMapper.Profile;
namespace TvNoms.Server.Services.Data.Models.Users;
namespace TvNoms.Core.Models.Users;
public class UserWithSessionModel : UserModel {
public bool EmailConfirmed { get; set; }

View File

@@ -0,0 +1,12 @@
using System.Linq.Expressions;
using TvNoms.Core.Entities;
namespace TvNoms.Core.Repositories;
public interface IClientRepository : IRepository<Client> {
Task DeactivateAsync(Client client, CancellationToken cancellationToken = default);
Task DeactivateManyAsync(Expression<Func<Client, bool>> predicate, CancellationToken cancellationToken = default);
Task DeactivateAllAsync(CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,6 @@
using TvNoms.Core.Entities;
namespace TvNoms.Core.Repositories;
public interface IMediaRepository : IRepository<Media> {
}

View File

@@ -1,8 +1,8 @@
using System.Linq.Expressions;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Data.Utilities;
using TvNoms.Core.Entities;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Services.Data.Repositories;
namespace TvNoms.Core.Repositories;
public interface IRepository<TEntity> where TEntity : class, IEntity {
Task<TEntity> CreateAsync(TEntity entity, CancellationToken cancellationToken = default);

View File

@@ -0,0 +1,7 @@
using TvNoms.Core.Entities;
namespace TvNoms.Core.Repositories;
public interface IRoleRepository : IRepository<Role> {
Task<Role?> GetByNameAsync(string name, CancellationToken cancellationToken = default);
}

View File

@@ -1,9 +1,9 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Identity;
using TvNoms.Core.Entities;
using TvNoms.Core.Extensions.Identity;
namespace TvNoms.Server.Services.Data.Repositories;
namespace TvNoms.Core.Repositories;
public interface IUserRepository : IRepository<User> {
Task AddLoginAsync(User user, UserLoginInfo login, CancellationToken cancellationToken = default);

View File

@@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Reference Include="AutoMapper">
<HintPath>..\..\..\..\..\..\home\fergalm\.nuget\packages\automapper\13.0.1\lib\net6.0\AutoMapper.dll</HintPath>
</Reference>
<Reference Include="FluentValidation">
<HintPath>..\..\..\..\..\..\home\fergalm\.nuget\packages\fluentvalidation\11.9.0\lib\net8.0\FluentValidation.dll</HintPath>
</Reference>
<Reference Include="Humanizer">
<HintPath>..\..\..\..\..\..\home\fergalm\.nuget\packages\humanizer.core\2.14.1\lib\net6.0\Humanizer.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNetCore.Authentication">
<HintPath>..\..\..\..\..\..\home\fergalm\.dotnet\shared\Microsoft.AspNetCore.App\8.0.3\Microsoft.AspNetCore.Authentication.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Identity.Core">
<HintPath>..\..\..\..\..\..\home\fergalm\.nuget\packages\microsoft.extensions.identity.core\8.0.3\lib\net8.0\Microsoft.Extensions.Identity.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Identity.Stores">
<HintPath>..\..\..\..\..\..\home\fergalm\.nuget\packages\microsoft.extensions.identity.stores\8.0.3\lib\net8.0\Microsoft.Extensions.Identity.Stores.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Options">
<HintPath>..\..\..\..\..\..\home\fergalm\.nuget\packages\microsoft.extensions.options\8.0.2\lib\net8.0\Microsoft.Extensions.Options.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Options">
<HintPath>..\..\..\..\..\..\home\fergalm\.nuget\packages\microsoft.extensions.options\8.0.2\lib\net8.0\Microsoft.Extensions.Options.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="DeviceId" Version="6.6.0"/>
<PackageReference Include="libphonenumber-csharp" Version="8.13.32"/>
<PackageReference Include="MediatR" Version="12.2.0"/>
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0"/>
<PackageReference Include="UAParser" Version="3.1.47"/>
</ItemGroup>
</Project>

View File

@@ -1,6 +1,6 @@
using System.Reflection;
namespace TvNoms.Server.Services.Utilities;
namespace TvNoms.Core.Utilities;
public static class AssemblyHelper {
public static IEnumerable<Assembly> GetAssemblies() {

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Data.Utilities;
namespace TvNoms.Core.Utilities;
public interface IPageable<T> : IEnumerable<T> {
long Offset { get; }

View File

@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
namespace TvNoms.Server.Services.Utilities;
namespace TvNoms.Core.Utilities;
public static class MimeTypes {
private const string DefaultFallbackMimeType = "application/octet-stream";

View File

@@ -1,6 +1,6 @@
using System.Collections;
namespace TvNoms.Server.Services.Data.Utilities;
namespace TvNoms.Core.Utilities;
public class Pageable<T> : IPageable<T> {
public Pageable(long offset, int limit, long total, IEnumerable<T> items) {

View File

@@ -1,6 +1,6 @@
using System.Linq.Expressions;
namespace TvNoms.Server.Services.Utilities;
namespace TvNoms.Core.Utilities;
public static class PredicateBuilder {
/// <summary>

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Data.Utilities;
namespace TvNoms.Core.Utilities;
public static class QueryableExtensions {
public static IQueryable<T> LongSkip<T>(this IQueryable<T> items, long count)

View File

@@ -1,11 +1,9 @@
namespace TvNoms.Server.Services.Utilities {
namespace TvNoms.Core.Utilities {
public static class ReflectionExtensions {
public static bool IsCompatibleWith(this Type type, Type otherType) {
if (otherType.IsGenericTypeDefinition) {
return type.IsAssignableToGenericTypeDefinition(otherType);
}
return otherType.IsAssignableFrom(type);
return otherType.IsGenericTypeDefinition
? type.IsAssignableToGenericTypeDefinition(otherType)
: otherType.IsAssignableFrom(type);
}
private static bool IsAssignableToGenericTypeDefinition(this Type type, Type genericType) {

View File

@@ -2,7 +2,7 @@ using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
namespace TvNoms.Server.Services.Infrastructure;
namespace TvNoms.Core.Utilities;
public static class Slugifier {
public static async Task<string> GenerateSlugAsync(string text, Func<string, Task<bool>> exists,

View File

@@ -1,6 +1,6 @@
using System.Text;
namespace TvNoms.Server.Services.Utilities;
namespace TvNoms.Core.Utilities;
public static class StreamExtensions {
public static async Task<byte[]> ToByteArrayAsync(this Stream stream) {

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Data.Utilities;
namespace TvNoms.Core.Utilities;
public static class UriExtensions {
public static Uri CombinePaths(this Uri uri, params string[] paths) {

View File

@@ -1,7 +1,7 @@
using System.Runtime.CompilerServices;
using Humanizer;
namespace TvNoms.Server.Services.Validation.Exceptions;
namespace TvNoms.Core.Utilities.Validation.Exceptions;
public class BadRequestException : StatusCodeException {
private const int STATUS_CODE = 400;

View File

@@ -1,7 +1,7 @@
using FluentValidation;
using FluentValidation.Results;
namespace TvNoms.Server.Services.Validation;
namespace TvNoms.Core.Utilities.Validation;
public static class ValidationExtensions {
public static IRuleBuilderOptionsConditions<T, string> Username<T>(this IRuleBuilder<T, string> ruleBuilder) {

View File

@@ -3,7 +3,7 @@ using System.Net.Mail;
using System.Text.RegularExpressions;
using PhoneNumbers;
namespace TvNoms.Server.Services.Validation;
namespace TvNoms.Core.Utilities.Validation;
public static class ValidationHelper {
public static MailAddress ParseEmail(string value) {

View File

@@ -1,17 +1,31 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
using TvNoms.Core.Utilities;
using TvNoms.Server.Data.Extensions;
namespace TvNoms.Server.Data;
public class AppDbContext(IConfiguration configuration) : DbContext {
public class AppDbContext(IConfiguration configuration) :
IdentityDbContext<User, Role, Guid, IdentityUserClaim<Guid>,
UserRole, IdentityUserLogin<Guid>, IdentityRoleClaim<Guid>, IdentityUserToken<Guid>> {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
optionsBuilder.UseNpgsql(configuration.GetConnectionString("DefaultConnection"));
protected override void OnModelCreating(ModelBuilder builder) {
base.OnModelCreating(builder);
var assemblies = AssemblyHelper.GetAssemblies();
public DbSet<User> Users { get; set; }
public DbSet<Client> Clients { get; set; }
public DbSet<Show> Shows { get; set; }
public DbSet<Movie> Movies { get; set; }
public DbSet<Media> Media { get; set; }
builder.ApplyEntities(assemblies);
builder.ApplyConfigurations(assemblies);
}
// public DbSet<User> Users { get; set; }
// public DbSet<UserRole> UserRoles { get; set; }
// public DbSet<Client> Clients { get; set; }
// public DbSet<Show> Shows { get; set; }
// public DbSet<Movie> Movies { get; set; }
// public DbSet<Media> Media { get; set; }
}

View File

@@ -0,0 +1,57 @@
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using TvNoms.Core.Entities;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Data.Extensions;
public static class DbContextExtensions {
public static ModelBuilder ApplyEntities(this ModelBuilder modelBuilder, IEnumerable<Assembly> assemblies,
Func<Type, bool>? predicate = null) {
var entityTypes = assemblies.SelectMany(_ => _.DefinedTypes).Select(_ => _.AsType())
.Where(type => type.IsClass && !type.IsAbstract && !type.IsGenericType &&
type.IsCompatibleWith(typeof(IEntity)) && (predicate?.Invoke(type) ?? true));
foreach (var entityType in entityTypes) {
modelBuilder.Entity(entityType);
}
return modelBuilder;
}
public static ModelBuilder ApplyConfigurations(this ModelBuilder modelBuilder, IEnumerable<Assembly> assemblies,
Func<Type, bool>? predicate = null) {
var entityTypeConfigurationTypes = assemblies.SelectMany(_ => _.DefinedTypes).Select(_ => _.AsType())
.Where(type => type.IsClass && !type.IsAbstract && !type.IsGenericType &&
type.IsCompatibleWith(typeof(IEntityTypeConfiguration<>)) && (predicate?.Invoke(type) ?? true));
var applyEntityConfigurationMethod = typeof(ModelBuilder)
.GetMethods()
.Single(
e => e.Name == nameof(ModelBuilder.ApplyConfiguration)
&& e.ContainsGenericParameters
&& e.GetParameters().SingleOrDefault()?.ParameterType.GetGenericTypeDefinition()
== typeof(IEntityTypeConfiguration<>));
foreach (var entityTypeConfigurationType in entityTypeConfigurationTypes) {
// Only accept types that contain a parameterless constructor, are not abstract and satisfy a predicate if it was used.
if (entityTypeConfigurationType.GetConstructor(Type.EmptyTypes) == null
|| (!predicate?.Invoke(entityTypeConfigurationType) ?? false)) {
continue;
}
foreach (var @interface in entityTypeConfigurationType.GetInterfaces()) {
if (!@interface.IsGenericType) {
continue;
}
if (@interface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)) {
var target = applyEntityConfigurationMethod.MakeGenericMethod(@interface.GenericTypeArguments[0]);
target.Invoke(modelBuilder, new[] { Activator.CreateInstance(entityTypeConfigurationType) });
}
}
}
return modelBuilder;
}
}

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Identity;
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Server.Data.Extensions;
public static class IdentityExtensions {
public static string GetMessage(this IEnumerable<IdentityError> errors) {

View File

@@ -1,342 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TvNoms.Server.Data;
#nullable disable
namespace TvNoms.Server.Data.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20240322175607_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TvNoms.Server.Data.Models.Client", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("Active")
.HasColumnType("boolean");
b.Property<string>("ConnectionId")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("ConnectionTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateCreated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<string>("DeviceId")
.HasColumnType("text");
b.Property<string>("IpAddress")
.HasColumnType("text");
b.Property<string>("UserAgent")
.HasColumnType("text");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Clients");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Media", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ContentType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateCreated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<int?>("Height")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("text");
b.Property<long>("Size")
.HasColumnType("bigint");
b.Property<int>("Type")
.HasColumnType("integer");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<int?>("Width")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("Media");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Movie", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Movies");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Role", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("NormalizedName")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Role");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Show", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Shows");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<bool>("Active")
.HasColumnType("boolean");
b.Property<Guid?>("AvatarId")
.HasColumnType("uuid");
b.Property<string>("Bio")
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("text");
b.Property<string>("Email")
.HasColumnType("text");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("EmailRequired")
.HasColumnType("boolean");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("LastActiveAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Location")
.HasColumnType("text");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasColumnType("text");
b.Property<string>("NormalizedUserName")
.HasColumnType("text");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<bool>("PhoneNumberRequired")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("AvatarId");
b.ToTable("Users");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.UserRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.HasIndex("UserId");
b.ToTable("UserRole");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Client", b =>
{
b.HasOne("TvNoms.Server.Data.Models.User", "User")
.WithMany("Clients")
.HasForeignKey("UserId");
b.Navigation("User");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.User", b =>
{
b.HasOne("TvNoms.Server.Data.Models.Media", "Avatar")
.WithMany()
.HasForeignKey("AvatarId");
b.Navigation("Avatar");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.UserRole", b =>
{
b.HasOne("TvNoms.Server.Data.Models.Role", "Role")
.WithMany("Users")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("TvNoms.Server.Data.Models.User", "User")
.WithMany("Roles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Role", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.User", b =>
{
b.Navigation("Clients");
b.Navigation("Roles");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -1,213 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TvNoms.Server.Data.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Media",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Size = table.Column<long>(type: "bigint", nullable: false),
Path = table.Column<string>(type: "text", nullable: false),
ContentType = table.Column<string>(type: "text", nullable: false),
Type = table.Column<int>(type: "integer", nullable: false),
Width = table.Column<int>(type: "integer", nullable: true),
Height = table.Column<int>(type: "integer", nullable: true),
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Media", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Movies",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Title = table.Column<string>(type: "text", nullable: false),
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movies", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Role",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "text", nullable: true),
NormalizedName = table.Column<string>(type: "text", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Role", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Shows",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Title = table.Column<string>(type: "text", nullable: false),
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Shows", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
FirstName = table.Column<string>(type: "text", nullable: false),
LastName = table.Column<string>(type: "text", nullable: false),
AvatarId = table.Column<Guid>(type: "uuid", nullable: true),
Bio = table.Column<string>(type: "text", nullable: true),
Location = table.Column<string>(type: "text", nullable: true),
Active = table.Column<bool>(type: "boolean", nullable: false),
LastActiveAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
EmailRequired = table.Column<bool>(type: "boolean", nullable: false),
PhoneNumberRequired = table.Column<bool>(type: "boolean", nullable: false),
UserName = table.Column<string>(type: "text", nullable: true),
NormalizedUserName = table.Column<string>(type: "text", nullable: true),
Email = table.Column<string>(type: "text", nullable: true),
NormalizedEmail = table.Column<string>(type: "text", nullable: true),
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
PasswordHash = table.Column<string>(type: "text", nullable: true),
SecurityStamp = table.Column<string>(type: "text", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
PhoneNumber = table.Column<string>(type: "text", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
table.ForeignKey(
name: "FK_Users_Media_AvatarId",
column: x => x.AvatarId,
principalTable: "Media",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "Clients",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
ConnectionId = table.Column<string>(type: "text", nullable: false),
ConnectionTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
IpAddress = table.Column<string>(type: "text", nullable: true),
DeviceId = table.Column<string>(type: "text", nullable: true),
UserId = table.Column<Guid>(type: "uuid", nullable: true),
UserAgent = table.Column<string>(type: "text", nullable: true),
Active = table.Column<bool>(type: "boolean", nullable: false),
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Clients", x => x.Id);
table.ForeignKey(
name: "FK_Clients_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "UserRole",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
RoleId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserRole", x => x.Id);
table.ForeignKey(
name: "FK_UserRole_Role_RoleId",
column: x => x.RoleId,
principalTable: "Role",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserRole_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Clients_UserId",
table: "Clients",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_UserRole_RoleId",
table: "UserRole",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "IX_UserRole_UserId",
table: "UserRole",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_Users_AvatarId",
table: "Users",
column: "AvatarId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Clients");
migrationBuilder.DropTable(
name: "Movies");
migrationBuilder.DropTable(
name: "Shows");
migrationBuilder.DropTable(
name: "UserRole");
migrationBuilder.DropTable(
name: "Role");
migrationBuilder.DropTable(
name: "Users");
migrationBuilder.DropTable(
name: "Media");
}
}
}

View File

@@ -0,0 +1,520 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TvNoms.Server.Data;
#nullable disable
namespace TvNoms.Server.Data.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20240322193356_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("TvNoms.Core.Entities.BaseEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<string>("Discriminator")
.IsRequired()
.HasMaxLength(13)
.HasColumnType("character varying(13)");
b.HasKey("Id");
b.ToTable("BaseEntity");
b.HasDiscriminator<string>("Discriminator").HasValue("BaseEntity");
b.UseTphMappingStrategy();
});
modelBuilder.Entity("TvNoms.Core.Entities.Role", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("TvNoms.Core.Entities.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<bool>("Active")
.HasColumnType("boolean");
b.Property<Guid?>("AvatarId")
.HasColumnType("uuid");
b.Property<string>("Bio")
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("EmailRequired")
.HasColumnType("boolean");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("LastActiveAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Location")
.HasColumnType("text");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<bool>("PhoneNumberRequired")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("AvatarId");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("TvNoms.Core.Entities.UserRole", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.Property<Guid?>("RoleId1")
.HasColumnType("uuid");
b.Property<Guid?>("UserId1")
.HasColumnType("uuid");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.HasIndex("RoleId1");
b.HasIndex("UserId1");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("TvNoms.Core.Entities.UserSession", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTimeOffset>("AccessTokenExpiresAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("AccessTokenHash")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("RefreshTokenExpiresAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("RefreshTokenHash")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserSession");
});
modelBuilder.Entity("TvNoms.Core.Entities.Client", b =>
{
b.HasBaseType("TvNoms.Core.Entities.BaseEntity");
b.Property<bool>("Active")
.HasColumnType("boolean");
b.Property<string>("ConnectionId")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("ConnectionTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("DeviceId")
.HasColumnType("text");
b.Property<string>("IpAddress")
.HasColumnType("text");
b.Property<string>("UserAgent")
.HasColumnType("text");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasIndex("UserId");
b.HasDiscriminator().HasValue("Client");
});
modelBuilder.Entity("TvNoms.Core.Entities.Media", b =>
{
b.HasBaseType("TvNoms.Core.Entities.BaseEntity");
b.Property<string>("ContentType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<int?>("Height")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("text");
b.Property<long>("Size")
.HasColumnType("bigint");
b.Property<int>("Type")
.HasColumnType("integer");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<int?>("Width")
.HasColumnType("integer");
b.HasDiscriminator().HasValue("Media");
});
modelBuilder.Entity("TvNoms.Core.Entities.Movie", b =>
{
b.HasBaseType("TvNoms.Core.Entities.BaseEntity");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasDiscriminator().HasValue("Movie");
});
modelBuilder.Entity("TvNoms.Core.Entities.Show", b =>
{
b.HasBaseType("TvNoms.Core.Entities.BaseEntity");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.ToTable("BaseEntity", t =>
{
t.Property("Title")
.HasColumnName("Show_Title");
});
b.HasDiscriminator().HasValue("Show");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("TvNoms.Core.Entities.Role", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TvNoms.Core.Entities.User", b =>
{
b.HasOne("TvNoms.Core.Entities.Media", "Avatar")
.WithMany()
.HasForeignKey("AvatarId");
b.Navigation("Avatar");
});
modelBuilder.Entity("TvNoms.Core.Entities.UserRole", b =>
{
b.HasOne("TvNoms.Core.Entities.Role", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("TvNoms.Core.Entities.Role", null)
.WithMany("Users")
.HasForeignKey("RoleId1");
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany("Roles")
.HasForeignKey("UserId1");
});
modelBuilder.Entity("TvNoms.Core.Entities.UserSession", b =>
{
b.HasOne("TvNoms.Core.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("TvNoms.Core.Entities.Client", b =>
{
b.HasOne("TvNoms.Core.Entities.User", "User")
.WithMany("Clients")
.HasForeignKey("UserId");
b.Navigation("User");
});
modelBuilder.Entity("TvNoms.Core.Entities.Role", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("TvNoms.Core.Entities.User", b =>
{
b.Navigation("Clients");
b.Navigation("Roles");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,353 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace TvNoms.Server.Data.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
RoleId = table.Column<Guid>(type: "uuid", nullable: false),
ClaimType = table.Column<string>(type: "text", nullable: true),
ClaimValue = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
ClaimType = table.Column<string>(type: "text", nullable: true),
ClaimValue = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(type: "text", nullable: false),
ProviderKey = table.Column<string>(type: "text", nullable: false),
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
UserId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<Guid>(type: "uuid", nullable: false),
RoleId = table.Column<Guid>(type: "uuid", nullable: false),
RoleId1 = table.Column<Guid>(type: "uuid", nullable: true),
UserId1 = table.Column<Guid>(type: "uuid", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId1",
column: x => x.RoleId1,
principalTable: "AspNetRoles",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
FirstName = table.Column<string>(type: "text", nullable: false),
LastName = table.Column<string>(type: "text", nullable: false),
AvatarId = table.Column<Guid>(type: "uuid", nullable: true),
Bio = table.Column<string>(type: "text", nullable: true),
Location = table.Column<string>(type: "text", nullable: true),
Active = table.Column<bool>(type: "boolean", nullable: false),
LastActiveAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
EmailRequired = table.Column<bool>(type: "boolean", nullable: false),
PhoneNumberRequired = table.Column<bool>(type: "boolean", nullable: false),
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
PasswordHash = table.Column<string>(type: "text", nullable: true),
SecurityStamp = table.Column<string>(type: "text", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
PhoneNumber = table.Column<string>(type: "text", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<Guid>(type: "uuid", nullable: false),
LoginProvider = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Value = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "BaseEntity",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
Discriminator = table.Column<string>(type: "character varying(13)", maxLength: 13, nullable: false),
ConnectionId = table.Column<string>(type: "text", nullable: true),
ConnectionTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
IpAddress = table.Column<string>(type: "text", nullable: true),
DeviceId = table.Column<string>(type: "text", nullable: true),
UserId = table.Column<Guid>(type: "uuid", nullable: true),
UserAgent = table.Column<string>(type: "text", nullable: true),
Active = table.Column<bool>(type: "boolean", nullable: true),
Name = table.Column<string>(type: "text", nullable: true),
Size = table.Column<long>(type: "bigint", nullable: true),
Path = table.Column<string>(type: "text", nullable: true),
ContentType = table.Column<string>(type: "text", nullable: true),
Type = table.Column<int>(type: "integer", nullable: true),
Width = table.Column<int>(type: "integer", nullable: true),
Height = table.Column<int>(type: "integer", nullable: true),
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
Title = table.Column<string>(type: "text", nullable: true),
Show_Title = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_BaseEntity", x => x.Id);
table.ForeignKey(
name: "FK_BaseEntity_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "UserSession",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
AccessTokenHash = table.Column<string>(type: "text", nullable: false),
AccessTokenExpiresAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
RefreshTokenHash = table.Column<string>(type: "text", nullable: false),
RefreshTokenExpiresAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserSession", x => x.Id);
table.ForeignKey(
name: "FK_UserSession_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId1",
table: "AspNetUserRoles",
column: "RoleId1");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_UserId1",
table: "AspNetUserRoles",
column: "UserId1");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "IX_AspNetUsers_AvatarId",
table: "AspNetUsers",
column: "AvatarId");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_BaseEntity_UserId",
table: "BaseEntity",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_UserSession_UserId",
table: "UserSession",
column: "UserId");
migrationBuilder.AddForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
table: "AspNetUserClaims",
column: "UserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
table: "AspNetUserLogins",
column: "UserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
table: "AspNetUserRoles",
column: "UserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId1",
table: "AspNetUserRoles",
column: "UserId1",
principalTable: "AspNetUsers",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_AspNetUsers_BaseEntity_AvatarId",
table: "AspNetUsers",
column: "AvatarId",
principalTable: "BaseEntity",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_BaseEntity_AspNetUsers_UserId",
table: "BaseEntity");
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "UserSession");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
migrationBuilder.DropTable(
name: "BaseEntity");
}
}
}

View File

@@ -22,99 +22,95 @@ namespace TvNoms.Server.Data.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TvNoms.Server.Data.Models.Client", b =>
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<Guid>("Id")
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.Property<bool>("Active")
.HasColumnType("boolean");
b.HasKey("Id");
b.Property<string>("ConnectionId")
.IsRequired()
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<DateTimeOffset>("ConnectionTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateCreated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<string>("DeviceId")
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("IpAddress")
.HasColumnType("text");
b.Property<string>("UserAgent")
.HasColumnType("text");
b.Property<Guid?>("UserId")
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Clients");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Media", b =>
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ContentType")
.IsRequired()
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<DateTime>("DateCreated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<DateTime>("DateUpdated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<int?>("Height")
.HasColumnType("integer");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Path")
.IsRequired()
b.Property<string>("Value")
.HasColumnType("text");
b.Property<long>("Size")
.HasColumnType("bigint");
b.HasKey("UserId", "LoginProvider", "Name");
b.Property<int>("Type")
.HasColumnType("integer");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<int?>("Width")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("Media");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Movie", b =>
modelBuilder.Entity("TvNoms.Core.Entities.BaseEntity", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
@@ -128,59 +124,48 @@ namespace TvNoms.Server.Data.Migrations
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<string>("Title")
b.Property<string>("Discriminator")
.IsRequired()
.HasColumnType("text");
.HasMaxLength(13)
.HasColumnType("character varying(13)");
b.HasKey("Id");
b.ToTable("Movies");
b.ToTable("BaseEntity");
b.HasDiscriminator<string>("Discriminator").HasValue("BaseEntity");
b.UseTphMappingStrategy();
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Role", b =>
modelBuilder.Entity("TvNoms.Core.Entities.Role", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasColumnType("text");
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.ToTable("Role");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Show", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("timestamp with time zone");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Shows");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.User", b =>
modelBuilder.Entity("TvNoms.Core.Entities.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
@@ -199,10 +184,12 @@ namespace TvNoms.Server.Data.Migrations
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Email")
.HasColumnType("text");
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
@@ -231,10 +218,12 @@ namespace TvNoms.Server.Data.Migrations
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasColumnType("text");
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasColumnType("text");
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
@@ -255,79 +244,268 @@ namespace TvNoms.Server.Data.Migrations
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasColumnType("text");
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("AvatarId");
b.ToTable("Users");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("TvNoms.Server.Data.Models.UserRole", b =>
modelBuilder.Entity("TvNoms.Core.Entities.UserRole", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.Property<Guid?>("RoleId1")
.HasColumnType("uuid");
b.Property<Guid?>("UserId1")
.HasColumnType("uuid");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.HasIndex("RoleId1");
b.HasIndex("UserId1");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("TvNoms.Core.Entities.UserSession", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.Property<DateTimeOffset>("AccessTokenExpiresAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("AccessTokenHash")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("RefreshTokenExpiresAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("RefreshTokenHash")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.HasIndex("UserId");
b.ToTable("UserSession");
});
modelBuilder.Entity("TvNoms.Core.Entities.Client", b =>
{
b.HasBaseType("TvNoms.Core.Entities.BaseEntity");
b.Property<bool>("Active")
.HasColumnType("boolean");
b.Property<string>("ConnectionId")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("ConnectionTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("DeviceId")
.HasColumnType("text");
b.Property<string>("IpAddress")
.HasColumnType("text");
b.Property<string>("UserAgent")
.HasColumnType("text");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasIndex("UserId");
b.ToTable("UserRole");
b.HasDiscriminator().HasValue("Client");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Client", b =>
modelBuilder.Entity("TvNoms.Core.Entities.Media", b =>
{
b.HasOne("TvNoms.Server.Data.Models.User", "User")
.WithMany("Clients")
.HasForeignKey("UserId");
b.HasBaseType("TvNoms.Core.Entities.BaseEntity");
b.Navigation("User");
b.Property<string>("ContentType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<int?>("Height")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Path")
.IsRequired()
.HasColumnType("text");
b.Property<long>("Size")
.HasColumnType("bigint");
b.Property<int>("Type")
.HasColumnType("integer");
b.Property<DateTimeOffset>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<int?>("Width")
.HasColumnType("integer");
b.HasDiscriminator().HasValue("Media");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.User", b =>
modelBuilder.Entity("TvNoms.Core.Entities.Movie", b =>
{
b.HasOne("TvNoms.Server.Data.Models.Media", "Avatar")
b.HasBaseType("TvNoms.Core.Entities.BaseEntity");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasDiscriminator().HasValue("Movie");
});
modelBuilder.Entity("TvNoms.Core.Entities.Show", b =>
{
b.HasBaseType("TvNoms.Core.Entities.BaseEntity");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.ToTable("BaseEntity", t =>
{
t.Property("Title")
.HasColumnName("Show_Title");
});
b.HasDiscriminator().HasValue("Show");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("TvNoms.Core.Entities.Role", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TvNoms.Core.Entities.User", b =>
{
b.HasOne("TvNoms.Core.Entities.Media", "Avatar")
.WithMany()
.HasForeignKey("AvatarId");
b.Navigation("Avatar");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.UserRole", b =>
modelBuilder.Entity("TvNoms.Core.Entities.UserRole", b =>
{
b.HasOne("TvNoms.Server.Data.Models.Role", "Role")
.WithMany("Users")
b.HasOne("TvNoms.Core.Entities.Role", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("TvNoms.Server.Data.Models.User", "User")
.WithMany("Roles")
b.HasOne("TvNoms.Core.Entities.Role", null)
.WithMany("Users")
.HasForeignKey("RoleId1");
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
b.HasOne("TvNoms.Core.Entities.User", null)
.WithMany("Roles")
.HasForeignKey("UserId1");
});
modelBuilder.Entity("TvNoms.Core.Entities.UserSession", b =>
{
b.HasOne("TvNoms.Core.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Role", b =>
modelBuilder.Entity("TvNoms.Core.Entities.Client", b =>
{
b.HasOne("TvNoms.Core.Entities.User", "User")
.WithMany("Clients")
.HasForeignKey("UserId");
b.Navigation("User");
});
modelBuilder.Entity("TvNoms.Core.Entities.Role", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.User", b =>
modelBuilder.Entity("TvNoms.Core.Entities.User", b =>
{
b.Navigation("Clients");

View File

@@ -1,3 +0,0 @@
namespace TvNoms.Server.Data.Models;
public record Movie(string Title) : BaseEntity;

View File

@@ -1,3 +0,0 @@
namespace TvNoms.Server.Data.Models;
public record Show(string Title) : BaseEntity;

View File

@@ -1,10 +1,10 @@
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using TvNoms.Server.Data;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Data.Utilities;
using TvNoms.Core.Entities;
using TvNoms.Core.Repositories;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Services.Data.Repositories;
namespace TvNoms.Server.Data.Repositories;
public abstract class AppRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity {
protected readonly AppDbContext _dbContext;

View File

@@ -1,17 +1,11 @@
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using TvNoms.Server.Data;
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
using TvNoms.Core.Repositories;
namespace TvNoms.Server.Services.Data.Repositories;
namespace TvNoms.Server.Data.Repositories;
public interface IClientRepository : IRepository<Client> {
Task DeactivateAsync(Client client, CancellationToken cancellationToken = default);
Task DeactivateManyAsync(Expression<Func<Client, bool>> predicate, CancellationToken cancellationToken = default);
Task DeactivateAllAsync(CancellationToken cancellationToken = default);
}
public class ClientRepository : AppRepository<Client>, IClientRepository {
public ClientRepository(AppDbContext dbContext) : base(dbContext) {

View File

@@ -0,0 +1,11 @@
using TvNoms.Core.Entities;
using TvNoms.Core.Repositories;
namespace TvNoms.Server.Data.Repositories;
public class MediaRepository : AppRepository<Media>, IMediaRepository {
public MediaRepository(AppDbContext dbContext) : base(dbContext) {
}
}

View File

@@ -1,13 +1,9 @@
using Microsoft.AspNetCore.Identity;
using TvNoms.Server.Data;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Identity;
using TvNoms.Core.Entities;
using TvNoms.Core.Repositories;
using TvNoms.Server.Data.Extensions;
namespace TvNoms.Server.Services.Data.Repositories;
public interface IRoleRepository : IRepository<Role> {
Task<Role?> GetByNameAsync(string name, CancellationToken cancellationToken = default);
}
namespace TvNoms.Server.Data.Repositories;
public class RoleRepository : AppRepository<Role>, IRoleRepository {
private readonly RoleManager<Role> _roleManager;

View File

@@ -1,8 +1,9 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using TvNoms.Server.Services.Utilities;
using TvNoms.Core.Repositories;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Services.Data.Repositories;
namespace TvNoms.Server.Data.Repositories;
public static class ServiceCollectionExtensions {
public static IServiceCollection AddRepositories(this IServiceCollection services, IEnumerable<Assembly> assemblies) {

View File

@@ -2,13 +2,14 @@ using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using TvNoms.Server.Data;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Identity;
using TvNoms.Server.Services.Infrastructure;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Entities;
using TvNoms.Core.Extensions.Identity;
using TvNoms.Core.Repositories;
using TvNoms.Core.Utilities;
using TvNoms.Core.Utilities.Validation;
using TvNoms.Server.Data.Extensions;
namespace TvNoms.Server.Services.Data.Repositories;
namespace TvNoms.Server.Data.Repositories;
public class UserRepository : AppRepository<User>, IUserRepository {
private readonly UserManager<User> _userManager;

View File

@@ -17,4 +17,8 @@
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TvNoms.Core\TvNoms.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -2,10 +2,11 @@ using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Primitives;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Validation;
using TvNoms.Core.Entities;
using TvNoms.Core.Extensions.Identity;
using TvNoms.Core.Utilities.Validation;
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Infrastructure.Identity;
public class ClientContext : IClientContext {
private readonly IHttpContextAccessor _httpContextAccessor;

View File

@@ -5,9 +5,11 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using TvNoms.Server.Services.Data.Repositories;
using TvNoms.Core.Extensions.Identity;
using TvNoms.Core.Repositories;
using TvNoms.Server.Data.Repositories;
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Infrastructure.Identity;
public class ConfigureJwtBearerOptions : IConfigureNamedOptions<JwtBearerOptions> {
private readonly IOptions<UserSessionOptions> _userSessionOptions;

View File

@@ -2,10 +2,11 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Data.Services;
using TvNoms.Core.Entities;
using TvNoms.Core.Extensions.Identity;
using TvNoms.Server.Services;
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Infrastructure.Identity;
public static class ServiceCollectionExtensions {
public static IServiceCollection AddWebAppCors(this IServiceCollection services) {

View File

@@ -1,9 +1,9 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Infrastructure.Identity;
public class UserClaimsPrincipalFactory(
UserManager<User> userManager,

View File

@@ -6,9 +6,10 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using TvNoms.Server.Data.Models;
using TvNoms.Core.Entities;
using TvNoms.Core.Extensions.Identity;
namespace TvNoms.Server.Services.Identity;
namespace TvNoms.Infrastructure.Identity;
public class UserSessionFactory : IUserSessionFactory {
private readonly JwtBearerOptions _jwtBearerOptions;

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Infrastructure.Messaging.Email;
namespace TvNoms.Infrastructure.Messaging.Email;
public class MailgunEmailOptions {
public string ApiKey { get; set; }

View File

@@ -3,12 +3,9 @@ using System.Net.Http.Headers;
using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TvNoms.Core.Extensions.EmailSender;
namespace TvNoms.Server.Services.Infrastructure.Messaging.Email;
public interface IEmailSender {
Task SendAsync(EmailMessage message, CancellationToken cancellationToken = default);
}
namespace TvNoms.Infrastructure.Messaging.Email;
public class MailgunEmailSender(IOptions<MailgunEmailOptions> emailSettings, ILogger<MailgunEmailSender> logger)
: IEmailSender {

View File

@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using TvNoms.Core.Extensions.EmailSender;
namespace TvNoms.Server.Services.Infrastructure.Messaging.Email;
namespace TvNoms.Infrastructure.Messaging.Email;
public static class ServiceCollectionExtensions {
public static IServiceCollection AddMailgunEmailSender(this IServiceCollection services,

View File

@@ -1,4 +1,6 @@
namespace TvNoms.Server.Services.Infrastructure.Messaging.SMS;
using TvNoms.Core.Extensions.SmsSender;
namespace TvNoms.Infrastructure.Messaging.SMS;
public class FakeSMSSender : ISmsSender {
public Task SendAsync(string phoneNumber, string message, CancellationToken cancellationToken = default) {

View File

@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using TvNoms.Core.Extensions.SmsSender;
namespace TvNoms.Server.Services.Infrastructure.Messaging.SMS;
namespace TvNoms.Infrastructure.Messaging.SMS;
public static class ServiceCollectionExtensions {
public static IServiceCollection AddFakeSmsSender(this IServiceCollection services) {

View File

@@ -1,8 +1,9 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using TvNoms.Server.Services.Data.Utilities;
using TvNoms.Core.FileStorage;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Services.Infrastructure.Storage;
namespace TvNoms.Infrastructure.Storage;
public class LocalFileStorage : IFileStorage {
private readonly IOptions<LocalFileStorageOptions> _storageOptions;

View File

@@ -1,4 +1,4 @@
namespace TvNoms.Server.Services.Infrastructure.Storage;
namespace TvNoms.Infrastructure.Storage;
public class LocalFileStorageOptions {
public string RootPath { get; set; } = default!;

View File

@@ -1,8 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Utilities;
using TvNoms.Core.Entities;
using TvNoms.Core.FileStorage;
using TvNoms.Core.Utilities;
namespace TvNoms.Server.Services.Infrastructure.Storage {
namespace TvNoms.Infrastructure.Storage {
public static class ServiceCollectionExtensions {
public static IServiceCollection AddLocalFileStorage(this IServiceCollection services,
Action<LocalFileStorageOptions> options) {
@@ -20,44 +21,40 @@ namespace TvNoms.Server.Services.Infrastructure.Storage {
public static IServiceCollection AddFileTypeOptions(this IServiceCollection services) {
services.Configure<FileRuleOptions>(options => {
options.Documents = new[] { ".doc", ".docx", ".rtf", ".pdf" }.Select(fileExtension => {
return new FileRule {
options.Documents = new[] { ".doc", ".docx", ".rtf", ".pdf" }.Select(fileExtension =>
new FileRule {
MediaType = MediaType.Document,
FileExtension = fileExtension,
FileSize = 83886080L,
ContentType = MimeTypes.GetMimeType(fileExtension)
};
})
})
.ToArray(); // Document - 80MB
options.Images = new[] { ".jpg", ".jpeg", ".png" }.Select(fileExtension => {
return new FileRule {
options.Images = new[] { ".jpg", ".jpeg", ".png" }.Select(fileExtension =>
new FileRule {
MediaType = MediaType.Image,
FileExtension = fileExtension,
FileSize = 5242880L,
ContentType = MimeTypes.GetMimeType(fileExtension)
};
})
})
.ToArray(); // Image - 5MB
options.Videos = new[] { ".mp4", ".webm", ".swf", ".flv" }.Select(fileExtension => {
return new FileRule {
options.Videos = new[] { ".mp4", ".webm", ".swf", ".flv" }.Select(fileExtension =>
new FileRule {
MediaType = MediaType.Video,
FileExtension = fileExtension,
FileSize = 524288000L,
ContentType = MimeTypes.GetMimeType(fileExtension)
};
})
})
.ToArray(); // Video - 500MB
options.Audios = new[] { ".mp3", ".ogg", ".wav" }.Select(fileExtension => {
return new FileRule {
options.Audios = new[] { ".mp3", ".ogg", ".wav" }.Select(fileExtension =>
new FileRule {
MediaType = MediaType.Audio,
FileExtension = fileExtension,
FileSize = 83886080L,
ContentType = MimeTypes.GetMimeType(fileExtension)
};
})
})
.ToArray(); // Audio - 80MB
});
return services;

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MailKit" Version="4.1.0"/>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.9"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TvNoms.Core\TvNoms.Core.csproj"/>
<ProjectReference Include="..\TvNoms.Data\TvNoms.Data.csproj"/>
<ProjectReference Include="..\TvNoms.Services\TvNoms.Services.csproj"/>
</ItemGroup>
</Project>

View File

@@ -8,8 +8,9 @@ using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using TvNoms.Core.Extensions.ViewRenderer;
namespace TvNoms.Server.Services.ViewRenderer.Razor;
namespace TvNoms.Infrastructure.ViewRenderer.Razor;
public class RazorViewRenderer : IViewRenderer {
private readonly IRazorViewEngine _viewEngine;

View File

@@ -0,0 +1,4 @@
namespace TvNoms.Infrastructure.ViewRenderer.Razor;
public static class RazorViewRendererConstants {
}

View File

@@ -0,0 +1,4 @@
namespace TvNoms.Infrastructure.ViewRenderer.Razor;
public class RazorViewRendererOptions {
}

Some files were not shown because too many files have changed in this diff Show More