diff --git a/Application/CommentService.cs b/Application/CommentService.cs index 279be44..df6249d 100644 --- a/Application/CommentService.cs +++ b/Application/CommentService.cs @@ -8,19 +8,21 @@ namespace Application { private readonly ILinkRepository _linkRepository; private readonly ICommentRepository _commentRepository; + private readonly ICurrentUserProvider _userProvider; private readonly IData _data; - public CommentService(ILinkRepository linkRepository, ICommentRepository commentRepository, IData data) + public CommentService(ILinkRepository linkRepository, ICommentRepository commentRepository, ICurrentUserProvider userProvider, IData data) { _linkRepository = linkRepository; _commentRepository = commentRepository; + _userProvider = userProvider; _data = data; } public Guid PublishComment(PublishCommentCommand cmd) { var link = _linkRepository.GetById(cmd.LinkId); - var comment = link.AddComment(cmd.Content); + var comment = link.AddComment(_userProvider.GetCurrentUserId(), cmd.Content); _commentRepository.Add(comment); diff --git a/Application/ICurrentUserProvider.cs b/Application/ICurrentUserProvider.cs new file mode 100644 index 0000000..40560a7 --- /dev/null +++ b/Application/ICurrentUserProvider.cs @@ -0,0 +1,9 @@ +using System; + +namespace Application +{ + public interface ICurrentUserProvider + { + Guid GetCurrentUserId(); + } +} \ No newline at end of file diff --git a/Application/LinkService.cs b/Application/LinkService.cs index 015a607..abda42b 100644 --- a/Application/LinkService.cs +++ b/Application/LinkService.cs @@ -10,11 +10,13 @@ namespace Application public class LinkService { private readonly ILinkRepository _linkRepository; + private readonly ICurrentUserProvider _userProvider; private readonly IData _data; - public LinkService(ILinkRepository linkRepository, IData data) + public LinkService(ILinkRepository linkRepository, ICurrentUserProvider userProvider, IData data) { _linkRepository = linkRepository; + _userProvider = userProvider; _data = data; } @@ -25,7 +27,7 @@ namespace Application /// public Guid PublishLink(PublishLinkCommand cmd) { - var link = new Link(cmd.Url); + var link = new Link(_userProvider.GetCurrentUserId(), cmd.Url); _linkRepository.Add(link); @@ -49,7 +51,7 @@ namespace Application private IQueryable LinksDTO() { return (from link in _data.Links - join comment in _data.Comments on link.Id equals comment.LinkId into comments + // join comment in _data.Comments on link.Id equals comment.LinkId into comments orderby link.CreatedAt descending select new LinkDTO { @@ -58,7 +60,7 @@ namespace Application CreatedAt = link.CreatedAt, UpvotesCount = 2, DownvotesCount = 4, - CommentsCount = comments.Count(), + CommentsCount = _data.Comments.Count(comment => comment.LinkId == link.Id), //comments.Count(), }); // return _data.Links diff --git a/Apps/Api/Startup.cs b/Apps/Api/Startup.cs index 83430b7..cbd403a 100644 --- a/Apps/Api/Startup.cs +++ b/Apps/Api/Startup.cs @@ -18,7 +18,8 @@ namespace Api // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { - services.AddHNServicesInMemory(); + // services.AddHNServicesInMemory(); + services.AddHNServicesEF(); services.AddControllers(options => { options.Filters.Add(); @@ -35,6 +36,7 @@ namespace Api // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + app.UseHNDatabaseMigrations(); app.UseOpenApi(); if (env.IsDevelopment()) diff --git a/Apps/Api/appsettings.json b/Apps/Api/appsettings.json index d9d9a9b..55f4a8f 100644 --- a/Apps/Api/appsettings.json +++ b/Apps/Api/appsettings.json @@ -6,5 +6,8 @@ "Microsoft.Hosting.Lifetime": "Information" } }, + "ConnectionStrings": { + "Default": "Data Source=../Website/hn.db" + }, "AllowedHosts": "*" } diff --git a/Apps/Website/Controllers/AccountController.cs b/Apps/Website/Controllers/AccountController.cs new file mode 100644 index 0000000..5b02ec5 --- /dev/null +++ b/Apps/Website/Controllers/AccountController.cs @@ -0,0 +1,87 @@ +using System.Threading.Tasks; +using Infrastructure.Identity; +using Infrastructure.Models; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; + +namespace Website.Controllers +{ + public class AccountController : BaseController + { + private readonly UserManager _userManager; + private readonly SignInManager _signinManager; + + public AccountController(UserManager userManager, SignInManager signinManager) + { + _userManager = userManager; + _signinManager = signinManager; + } + + [AllowAnonymous] + public IActionResult Register() + { + return View(); + } + + [HttpPost] + [ValidateAntiForgeryToken] + [AllowAnonymous] + public async Task Register(RegisterViewModel cmd) + { + if (!ModelState.IsValid) + { + return View(cmd); + } + + var user = new User { UserName = cmd.Username }; + var result = await _userManager.CreateAsync(user, cmd.Password); + + if (!result.Succeeded) + { + ModelState.AddModelError(nameof(RegisterViewModel.Username), "could not register"); + return View(cmd); + } + + Success("Your account was created, you can now login"); + + return RedirectToAction(nameof(Login)); + } + + [AllowAnonymous] + public IActionResult Login() + { + return View(); + } + + [HttpPost] + [ValidateAntiForgeryToken] + [AllowAnonymous] + public async Task Login(LoginViewModel cmd) + { + if (!ModelState.IsValid) + { + return View(cmd); + } + + var result = await _signinManager.PasswordSignInAsync(cmd.Username, cmd.Password, true, false); + + if (!result.Succeeded) + { + ModelState.AddModelError(nameof(LoginViewModel.Username), "Could not sign you in, please retry"); + return View(cmd); + } + + Success("You're now logged in"); + + return Redirect("/"); + } + + public async Task Logout() + { + await _signinManager.SignOutAsync(); + Success("You're now logged out"); + return Redirect("/"); + } + } +} \ No newline at end of file diff --git a/Apps/Website/Controllers/CommentsController.cs b/Apps/Website/Controllers/CommentsController.cs index bf6076d..f35d381 100644 --- a/Apps/Website/Controllers/CommentsController.cs +++ b/Apps/Website/Controllers/CommentsController.cs @@ -1,5 +1,6 @@ using System; using Application; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Website.Controllers diff --git a/Apps/Website/Controllers/HomeController.cs b/Apps/Website/Controllers/HomeController.cs index dbcfdca..a9ccc77 100644 --- a/Apps/Website/Controllers/HomeController.cs +++ b/Apps/Website/Controllers/HomeController.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Website.Models; namespace Website.Controllers { + [AllowAnonymous] public class HomeController : Controller { private readonly ILogger _logger; diff --git a/Apps/Website/Controllers/LinksController.cs b/Apps/Website/Controllers/LinksController.cs index e18a9f6..4c18498 100644 --- a/Apps/Website/Controllers/LinksController.cs +++ b/Apps/Website/Controllers/LinksController.cs @@ -1,5 +1,6 @@ using System; using Application; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Website.Models; @@ -16,6 +17,7 @@ namespace Website _commentService = commentService; } + [AllowAnonymous] public IActionResult Index() { // return Forbid(); @@ -23,6 +25,7 @@ namespace Website } [HttpGet("{controller}/detail/{linkId:guid}")] + [AllowAnonymous] // [TypeFilter(typeof(CustomExceptionFilter))] // Permet d'appliquer un filtre ASP.Net // sur une action uniquement (valable pour toutes les actions d'un controlleur si appliqué sur ce dernier) public IActionResult Show(Guid linkId) diff --git a/Apps/Website/Startup.cs b/Apps/Website/Startup.cs index 9c5fc09..568e1f7 100644 --- a/Apps/Website/Startup.cs +++ b/Apps/Website/Startup.cs @@ -2,8 +2,10 @@ using Application; using Domain; using Infrastructure; using Infrastructure.Filters; +using Infrastructure.Identity; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -25,15 +27,33 @@ namespace Website // ServiceCollectionExtensions.AddHNServices(services); // strictement équivalent à la ligne du dessous // services.AddHNServicesInMemory(); services.AddHNServicesEF(); + services + .AddIdentity(options => + { + // FIXME uniquement pour nos besoins :) + options.Password.RequiredLength = options.Password.RequiredUniqueChars = 0; + options.Password.RequireDigit = options.Password.RequireLowercase = options.Password.RequireUppercase = options.Password.RequireNonAlphanumeric = false; + }) + .AddEntityFrameworkStores(); + + services.AddAuthorization(options => + { + options.AddPolicy("IsAdmin", policy => policy + .RequireUserName("test")); + }); + services.AddControllersWithViews(options => { options.Filters.Add(); + options.Filters.Add(new AuthorizeFilter()); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + app.UseHNDatabaseMigrations(); + // app.Use(async (context, next) => // { // try @@ -69,6 +89,7 @@ namespace Website // Console.WriteLine("<<< Ho"); // }); + app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => diff --git a/Apps/Website/Views/Account/Login.cshtml b/Apps/Website/Views/Account/Login.cshtml new file mode 100644 index 0000000..c18f609 --- /dev/null +++ b/Apps/Website/Views/Account/Login.cshtml @@ -0,0 +1,18 @@ +@model Infrastructure.Models.LoginViewModel +@{ + ViewData["Title"] = "Login"; +} + +

Login

+ +
+ + + + + + + + + +
\ No newline at end of file diff --git a/Apps/Website/Views/Account/Register.cshtml b/Apps/Website/Views/Account/Register.cshtml new file mode 100644 index 0000000..e3b9e5a --- /dev/null +++ b/Apps/Website/Views/Account/Register.cshtml @@ -0,0 +1,23 @@ +@model Infrastructure.Models.RegisterViewModel +@{ + ViewData["Title"] = "Register an account"; +} + +

Register an account

+ +
+ + + + + + + + + + + + + +
+ diff --git a/Apps/Website/Views/Shared/_Layout.cshtml b/Apps/Website/Views/Shared/_Layout.cshtml index b87ce1b..60d178f 100644 --- a/Apps/Website/Views/Shared/_Layout.cshtml +++ b/Apps/Website/Views/Shared/_Layout.cshtml @@ -24,6 +24,22 @@ + @if (!User.Identity.IsAuthenticated) + { + + + } + else + { + + + } diff --git a/Apps/Website/appsettings.Development.json b/Apps/Website/appsettings.Development.json index 8983e0f..eaa8762 100644 --- a/Apps/Website/appsettings.Development.json +++ b/Apps/Website/appsettings.Development.json @@ -3,7 +3,8 @@ "LogLevel": { "Default": "Information", "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Microsoft.Hosting.Lifetime": "Information", + "Microsoft.EntityFrameworkCore": "Information" } } } diff --git a/Apps/Website/appsettings.json b/Apps/Website/appsettings.json index d9d9a9b..3eb4436 100644 --- a/Apps/Website/appsettings.json +++ b/Apps/Website/appsettings.json @@ -6,5 +6,8 @@ "Microsoft.Hosting.Lifetime": "Information" } }, + "ConnectionStrings": { + "Default": "Data Source=./hn.db" + }, "AllowedHosts": "*" } diff --git a/Apps/Website/hn.db b/Apps/Website/hn.db new file mode 100644 index 0000000..8a35307 Binary files /dev/null and b/Apps/Website/hn.db differ diff --git a/Apps/Website/hn.db-shm b/Apps/Website/hn.db-shm new file mode 100644 index 0000000..65e88d0 Binary files /dev/null and b/Apps/Website/hn.db-shm differ diff --git a/Apps/Website/hn.db-wal b/Apps/Website/hn.db-wal new file mode 100644 index 0000000..8878a3a Binary files /dev/null and b/Apps/Website/hn.db-wal differ diff --git a/Domain/Comment.cs b/Domain/Comment.cs index 518a113..2ade782 100644 --- a/Domain/Comment.cs +++ b/Domain/Comment.cs @@ -8,10 +8,12 @@ namespace Domain public Guid LinkId { get; } public string Content { get; } public DateTime CreatedAt { get; } + public Guid CreatedBy { get; } - internal Comment(Guid linkId, string content) + internal Comment(Guid linkId, Guid createdBy, string content) { Id = Guid.NewGuid(); + CreatedBy = createdBy; LinkId = linkId; Content = content; CreatedAt = DateTime.UtcNow; diff --git a/Domain/Link.cs b/Domain/Link.cs index dd5cb04..d8c2a9f 100644 --- a/Domain/Link.cs +++ b/Domain/Link.cs @@ -10,12 +10,14 @@ namespace Domain public Guid Id { get; } public string Url { get; } public DateTime CreatedAt { get; } + public Guid CreatedBy { get; } - public Link(string url) + public Link(Guid createdBy, string url) { Id = Guid.NewGuid(); CreatedAt = DateTime.UtcNow; Url = url; + CreatedBy = createdBy; } /// @@ -24,9 +26,9 @@ namespace Domain /// /// /// - public Comment AddComment(string content) + public Comment AddComment(Guid createdBy, string content) { - return new Comment(this.Id, content); + return new Comment(this.Id, createdBy, content); } } } diff --git a/Infrastructure/ApplicationBuilderExtensions.cs b/Infrastructure/ApplicationBuilderExtensions.cs new file mode 100644 index 0000000..b9ec7ec --- /dev/null +++ b/Infrastructure/ApplicationBuilderExtensions.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Infrastructure +{ + public static class ApplicationBuilderExtensions + { + public static IApplicationBuilder UseHNDatabaseMigrations(this IApplicationBuilder builder) + { + using var scope = builder.ApplicationServices.CreateScope(); + using var context = scope.ServiceProvider.GetRequiredService(); + context.Database.Migrate(); + + return builder; + } + } +} \ No newline at end of file diff --git a/Infrastructure/EntityTypes/CommentEntityType.cs b/Infrastructure/EntityTypes/CommentEntityType.cs index d28cc24..fb18da7 100644 --- a/Infrastructure/EntityTypes/CommentEntityType.cs +++ b/Infrastructure/EntityTypes/CommentEntityType.cs @@ -1,4 +1,5 @@ using Domain; +using Infrastructure.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -16,6 +17,11 @@ namespace Infrastructure.EntityTypes .HasForeignKey(o => o.LinkId) .IsRequired() .OnDelete(DeleteBehavior.Cascade); + builder.HasOne() + .WithMany() + .HasForeignKey(o => o.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); } } } \ No newline at end of file diff --git a/Infrastructure/EntityTypes/LinkEntityType.cs b/Infrastructure/EntityTypes/LinkEntityType.cs index 0798d76..82b20d5 100644 --- a/Infrastructure/EntityTypes/LinkEntityType.cs +++ b/Infrastructure/EntityTypes/LinkEntityType.cs @@ -1,4 +1,5 @@ using Domain; +using Infrastructure.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -11,6 +12,11 @@ namespace Infrastructure.EntityTypes builder.HasKey(o => o.Id); builder.Property(o => o.Url).HasMaxLength(250).IsRequired(); builder.Property(o => o.CreatedAt).IsRequired(); + builder.HasOne() + .WithMany() + .HasForeignKey(o => o.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); } } } \ No newline at end of file diff --git a/Infrastructure/HNDbContext.cs b/Infrastructure/HNDbContext.cs index 6997ea6..1e80adb 100644 --- a/Infrastructure/HNDbContext.cs +++ b/Infrastructure/HNDbContext.cs @@ -1,12 +1,14 @@ +using System; using System.Linq; using Application; using Domain; -using Infrastructure.EntityTypes; +using Infrastructure.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; namespace Infrastructure { - public class HNDbContext : DbContext, IData + public class HNDbContext : IdentityDbContext, IData { public DbSet Links { get; set; } public DbSet Comments { get; set; } diff --git a/Infrastructure/HttpCurrentUserProvider.cs b/Infrastructure/HttpCurrentUserProvider.cs new file mode 100644 index 0000000..62e0e99 --- /dev/null +++ b/Infrastructure/HttpCurrentUserProvider.cs @@ -0,0 +1,26 @@ +using System; +using Application; +using Infrastructure.Identity; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; + +namespace Infrastructure +{ + public class HttpCurrentUserProvider : ICurrentUserProvider + { + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly UserManager _userManager; + + public HttpCurrentUserProvider(IHttpContextAccessor httpContextAccessor, UserManager userManager) + { + _httpContextAccessor = httpContextAccessor; + _userManager = userManager; + } + + public Guid GetCurrentUserId() + { + var userPrincipal = _httpContextAccessor.HttpContext.User; + return Guid.Parse(_userManager.GetUserId(userPrincipal)); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Identity/Role.cs b/Infrastructure/Identity/Role.cs new file mode 100644 index 0000000..3553019 --- /dev/null +++ b/Infrastructure/Identity/Role.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.AspNetCore.Identity; + +namespace Infrastructure.Identity +{ + public class Role : IdentityRole + { + + } +} \ No newline at end of file diff --git a/Infrastructure/Identity/User.cs b/Infrastructure/Identity/User.cs new file mode 100644 index 0000000..d696a93 --- /dev/null +++ b/Infrastructure/Identity/User.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.AspNetCore.Identity; + +namespace Infrastructure.Identity +{ + public class User : IdentityUser + { + // On peut ajouter des propriétés ici au besoin + } +} \ No newline at end of file diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj index 496090f..9e0f06e 100644 --- a/Infrastructure/Infrastructure.csproj +++ b/Infrastructure/Infrastructure.csproj @@ -9,6 +9,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Infrastructure/Migrations/20210429074555_AddAspNetIdentity.Designer.cs b/Infrastructure/Migrations/20210429074555_AddAspNetIdentity.Designer.cs new file mode 100644 index 0000000..4e34500 --- /dev/null +++ b/Infrastructure/Migrations/20210429074555_AddAspNetIdentity.Designer.cs @@ -0,0 +1,316 @@ +// +using System; +using Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(HNDbContext))] + [Migration("20210429074555_AddAspNetIdentity")] + partial class AddAspNetIdentity + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.5"); + + modelBuilder.Entity("Domain.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("LinkId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LinkId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("Domain.Link", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Links"); + }); + + modelBuilder.Entity("Infrastructure.Identity.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Infrastructure.Identity.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Domain.Comment", b => + { + b.HasOne("Domain.Link", null) + .WithMany() + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Infrastructure.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Infrastructure.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Infrastructure/Migrations/20210429074555_AddAspNetIdentity.cs b/Infrastructure/Migrations/20210429074555_AddAspNetIdentity.cs new file mode 100644 index 0000000..f34f568 --- /dev/null +++ b/Infrastructure/Migrations/20210429074555_AddAspNetIdentity.cs @@ -0,0 +1,217 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Infrastructure.Migrations +{ + public partial class AddAspNetIdentity : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "INTEGER", nullable: false), + PasswordHash = table.Column(type: "TEXT", nullable: true), + SecurityStamp = table.Column(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), + PhoneNumber = table.Column(type: "TEXT", nullable: true), + PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), + LockoutEnd = table.Column(type: "TEXT", nullable: true), + LockoutEnabled = table.Column(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RoleId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(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(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "TEXT", nullable: false), + ProviderKey = table.Column(type: "TEXT", nullable: false), + ProviderDisplayName = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) + }, + 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_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + LoginProvider = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Value = table.Column(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.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: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/Infrastructure/Migrations/20210429093017_AddCreatedBy.Designer.cs b/Infrastructure/Migrations/20210429093017_AddCreatedBy.Designer.cs new file mode 100644 index 0000000..c0bd0fb --- /dev/null +++ b/Infrastructure/Migrations/20210429093017_AddCreatedBy.Designer.cs @@ -0,0 +1,341 @@ +// +using System; +using Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(HNDbContext))] + [Migration("20210429093017_AddCreatedBy")] + partial class AddCreatedBy + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.5"); + + modelBuilder.Entity("Domain.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("LinkId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("LinkId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("Domain.Link", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.ToTable("Links"); + }); + + modelBuilder.Entity("Infrastructure.Identity.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Infrastructure.Identity.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Domain.Comment", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Link", null) + .WithMany() + .HasForeignKey("LinkId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Domain.Link", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Infrastructure.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Infrastructure.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Infrastructure/Migrations/20210429093017_AddCreatedBy.cs b/Infrastructure/Migrations/20210429093017_AddCreatedBy.cs new file mode 100644 index 0000000..a011471 --- /dev/null +++ b/Infrastructure/Migrations/20210429093017_AddCreatedBy.cs @@ -0,0 +1,81 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Infrastructure.Migrations +{ + public partial class AddCreatedBy : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DELETE FROM Comments;"); + migrationBuilder.Sql("DELETE FROM Links;"); + + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "Links", + type: "TEXT", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "Comments", + type: "TEXT", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.CreateIndex( + name: "IX_Links_CreatedBy", + table: "Links", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_Comments_CreatedBy", + table: "Comments", + column: "CreatedBy"); + + migrationBuilder.AddForeignKey( + name: "FK_Comments_AspNetUsers_CreatedBy", + table: "Comments", + column: "CreatedBy", + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Links_AspNetUsers_CreatedBy", + table: "Links", + column: "CreatedBy", + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Comments_AspNetUsers_CreatedBy", + table: "Comments"); + + migrationBuilder.DropForeignKey( + name: "FK_Links_AspNetUsers_CreatedBy", + table: "Links"); + + migrationBuilder.DropIndex( + name: "IX_Links_CreatedBy", + table: "Links"); + + migrationBuilder.DropIndex( + name: "IX_Comments_CreatedBy", + table: "Comments"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "Links"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "Comments"); + } + } +} diff --git a/Infrastructure/Migrations/HNDbContextModelSnapshot.cs b/Infrastructure/Migrations/HNDbContextModelSnapshot.cs index baf7ca5..0cfe611 100644 --- a/Infrastructure/Migrations/HNDbContextModelSnapshot.cs +++ b/Infrastructure/Migrations/HNDbContextModelSnapshot.cs @@ -29,11 +29,16 @@ namespace Infrastructure.Migrations b.Property("CreatedAt") .HasColumnType("TEXT"); + b.Property("CreatedBy") + .HasColumnType("TEXT"); + b.Property("LinkId") .HasColumnType("TEXT"); b.HasKey("Id"); + b.HasIndex("CreatedBy"); + b.HasIndex("LinkId"); b.ToTable("Comments"); @@ -48,6 +53,9 @@ namespace Infrastructure.Migrations b.Property("CreatedAt") .HasColumnType("TEXT"); + b.Property("CreatedBy") + .HasColumnType("TEXT"); + b.Property("Url") .IsRequired() .HasMaxLength(250) @@ -55,17 +63,276 @@ namespace Infrastructure.Migrations b.HasKey("Id"); + b.HasIndex("CreatedBy"); + b.ToTable("Links"); }); + modelBuilder.Entity("Infrastructure.Identity.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Infrastructure.Identity.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + modelBuilder.Entity("Domain.Comment", b => { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + b.HasOne("Domain.Link", null) .WithMany() .HasForeignKey("LinkId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); + + modelBuilder.Entity("Domain.Link", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Infrastructure.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Infrastructure.Identity.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Infrastructure.Identity.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); #pragma warning restore 612, 618 } } diff --git a/Infrastructure/Models/LoginViewModel.cs b/Infrastructure/Models/LoginViewModel.cs new file mode 100644 index 0000000..effa663 --- /dev/null +++ b/Infrastructure/Models/LoginViewModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace Infrastructure.Models +{ + public class LoginViewModel + { + [Required] + public string Username { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/Infrastructure/Models/RegisterViewModel.cs b/Infrastructure/Models/RegisterViewModel.cs new file mode 100644 index 0000000..ee506cd --- /dev/null +++ b/Infrastructure/Models/RegisterViewModel.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; + +namespace Infrastructure.Models +{ + public class RegisterViewModel + { + [Required] + public string Username { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + [Required] + [Compare(nameof(Password))] + [DataType(DataType.Password)] + public string ConfirmPassword { get; set; } + } +} \ No newline at end of file diff --git a/Infrastructure/ServiceCollectionExtensions.cs b/Infrastructure/ServiceCollectionExtensions.cs index 50d548d..fdb9a0d 100644 --- a/Infrastructure/ServiceCollectionExtensions.cs +++ b/Infrastructure/ServiceCollectionExtensions.cs @@ -1,3 +1,4 @@ +using System; using Application; using Domain; using Microsoft.EntityFrameworkCore; @@ -20,6 +21,8 @@ namespace Infrastructure { var configuration = provider.GetRequiredService(); + // configuration["ConnectionStrings:Default"] + options.UseSqlite(configuration.GetConnectionString("Default")); }); @@ -38,9 +41,10 @@ namespace Infrastructure /// public static IServiceCollection AddHNServicesInMemory(this IServiceCollection services) { - var link1 = new Domain.Link("http://default.website"); - var link2 = new Domain.Link("http://another.website"); - var link3 = new Domain.Link("http://a.final.website"); + var uid = Guid.NewGuid(); + var link1 = new Domain.Link(uid, "http://default.website"); + var link2 = new Domain.Link(uid, "http://another.website"); + var link3 = new Domain.Link(uid, "http://a.final.website"); services.AddSingleton(new Infrastructure.Repositories.Memory.LinkRepository( link1, @@ -48,8 +52,8 @@ namespace Infrastructure link3 )); services.AddSingleton(new Infrastructure.Repositories.Memory.CommentRepository( - link1.AddComment("my first comment"), - link3.AddComment("another comment") + link1.AddComment(uid, "my first comment"), + link3.AddComment(uid, "another comment") )); services.AddSingleton(serviceProvider => { @@ -69,6 +73,7 @@ namespace Infrastructure /// private static IServiceCollection AddCommon(this IServiceCollection services) { + services.AddScoped(); services.AddTransient(); services.AddTransient();