ajout auteur des liens / commentaires
This commit is contained in:
parent
9399b6d92c
commit
5b1f99bae1
@ -1,6 +1,9 @@
|
|||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
using HackerNet.Api.Models;
|
using HackerNet.Api.Models;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
namespace HackerNet.Api.Controllers;
|
namespace HackerNet.Api.Controllers;
|
||||||
|
|
||||||
@ -10,17 +13,21 @@ public class AccountsController : ControllerBase
|
|||||||
{
|
{
|
||||||
private readonly UserManager<IdentityUser> _userManager;
|
private readonly UserManager<IdentityUser> _userManager;
|
||||||
private readonly SignInManager<IdentityUser> _signInManager;
|
private readonly SignInManager<IdentityUser> _signInManager;
|
||||||
|
private readonly IUserClaimsPrincipalFactory<IdentityUser> _claimsFactory;
|
||||||
|
private readonly TokenValidation _tokenValidation;
|
||||||
|
|
||||||
public AccountsController(UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager)
|
public AccountsController(UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager, IUserClaimsPrincipalFactory<IdentityUser> claimsFactory, TokenValidation tokenValidation)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
|
_claimsFactory = claimsFactory;
|
||||||
|
_tokenValidation = tokenValidation;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("me")]
|
[HttpGet("me")]
|
||||||
public ActionResult Me()
|
public ActionResult<string> Me()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return User.Identity.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
@ -36,4 +43,36 @@ public class AccountsController : ControllerBase
|
|||||||
|
|
||||||
return CreatedAtAction(nameof(Me), null);
|
return CreatedAtAction(nameof(Me), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("token")]
|
||||||
|
public async Task<ActionResult<string>> Signin(SignupLoginViewModel cmd)
|
||||||
|
{
|
||||||
|
var user = await _userManager.FindByNameAsync(cmd.Username);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _signInManager.CheckPasswordSignInAsync(user, cmd.Password, false);
|
||||||
|
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
var principal = await _claimsFactory.CreateAsync(user);
|
||||||
|
var tokenDescriptor = new SecurityTokenDescriptor
|
||||||
|
{
|
||||||
|
Subject = (ClaimsIdentity)principal.Identity,
|
||||||
|
Expires = DateTime.UtcNow.AddDays(7),
|
||||||
|
Issuer = _tokenValidation.Issuer,
|
||||||
|
Audience = _tokenValidation.Audience,
|
||||||
|
SigningCredentials = new SigningCredentials(
|
||||||
|
new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_tokenValidation.Key))
|
||||||
|
, SecurityAlgorithms.HmacSha256Signature)
|
||||||
|
};
|
||||||
|
|
||||||
|
return new JwtSecurityTokenHandler().CreateEncodedJwt(tokenDescriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using HackerNet.Application;
|
using HackerNet.Application;
|
||||||
using HackerNet.Infrastructure.AspNet.Filters;
|
using HackerNet.Infrastructure.AspNet.Filters;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace HackerNet.Api.Controllers;
|
namespace HackerNet.Api.Controllers;
|
||||||
@ -52,6 +53,7 @@ public class LinksController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id:guid}/comments")]
|
[HttpPost("{id:guid}/comments")]
|
||||||
|
[Authorize]
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
public ActionResult PublishComment(Guid id, PublishCommentBody cmd)
|
public ActionResult PublishComment(Guid id, PublishCommentBody cmd)
|
||||||
@ -76,6 +78,7 @@ public class LinksController : ControllerBase
|
|||||||
/// <param name="cmd"></param>
|
/// <param name="cmd"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
[Authorize]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
public ActionResult PublishLink(PublishLinkCommand cmd)
|
public ActionResult PublishLink(PublishLinkCommand cmd)
|
||||||
|
|||||||
@ -12,6 +12,7 @@ var tokenValidation = builder.Configuration
|
|||||||
.GetSection("TokenValidation")
|
.GetSection("TokenValidation")
|
||||||
.Get<TokenValidation>();
|
.Get<TokenValidation>();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton(tokenValidation);
|
||||||
builder.Services.AddHackerNetServicesEntityFramework(builder.Configuration);
|
builder.Services.AddHackerNetServicesEntityFramework(builder.Configuration);
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services
|
builder.Services
|
||||||
|
|||||||
@ -6,6 +6,7 @@ GET {{url}}/api/links
|
|||||||
|
|
||||||
POST {{url}}/api/links
|
POST {{url}}/api/links
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiJiMGFiMDU0Zi1lMjI0LTRlNGMtYmY0MC0xY2UxNzU0ZWNiMTciLCJuYW1lIjoidGVzdCIsIkFzcE5ldC5JZGVudGl0eS5TZWN1cml0eVN0YW1wIjoiRlNHVk01U0ZFRUpSTzVORFIzR0ZPN1ZRWkRJQ1c2QVEiLCJuYmYiOjE2Mzk1Nzg3NjEsImV4cCI6MTY0MDE4MzU2MSwiaWF0IjoxNjM5NTc4NzYxLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo3MjUyLyIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjcyNTIvIn0.wjL5ALncw5JtNe5YpZD-uQAz2-BvbznOqqN1xgZpjAU
|
||||||
|
|
||||||
{
|
{
|
||||||
"url": "https://localhost:7252/api/links",
|
"url": "https://localhost:7252/api/links",
|
||||||
@ -30,6 +31,16 @@ Content-Type: application/json
|
|||||||
POST {{url}}/api/accounts
|
POST {{url}}/api/accounts
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"username": "test",
|
||||||
|
"password": "G6:c`bzr2h#Pq;4"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
POST {{url}}/api/accounts/token
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"username": "test",
|
"username": "test",
|
||||||
"password": "G6:c`bzr2h#Pq;4"
|
"password": "G6:c`bzr2h#Pq;4"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Debug",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -9,5 +9,6 @@
|
|||||||
<a class="text-indigo-500 hover:underline" asp-controller="Links" asp-action="Detail" asp-route-id="@Model.Id">Détail du lien</a>
|
<a class="text-indigo-500 hover:underline" asp-controller="Links" asp-action="Detail" asp-route-id="@Model.Id">Détail du lien</a>
|
||||||
<a class="text-indigo-500 hover:underline" asp-controller="Comments" asp-action="New" asp-route-id="@Model.Id">Ajouter un commentaire</a>
|
<a class="text-indigo-500 hover:underline" asp-controller="Comments" asp-action="New" asp-route-id="@Model.Id">Ajouter un commentaire</a>
|
||||||
<p>@Model.CommentsCount commentaire@(Model.CommentsCount > 1 ? "s" : "")</p>
|
<p>@Model.CommentsCount commentaire@(Model.CommentsCount > 1 ? "s" : "")</p>
|
||||||
|
<p>@Model.Author</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@ -7,20 +7,25 @@ public class LinkService
|
|||||||
private readonly ILinkRepository _repository;
|
private readonly ILinkRepository _repository;
|
||||||
private readonly ICommentRepository _commentRepository;
|
private readonly ICommentRepository _commentRepository;
|
||||||
private readonly IReadStore _readStore;
|
private readonly IReadStore _readStore;
|
||||||
|
private readonly ICurrentUser _currentUser;
|
||||||
|
|
||||||
public LinkService(
|
public LinkService(
|
||||||
ILinkRepository repository,
|
ILinkRepository repository,
|
||||||
ICommentRepository commentRepository,
|
ICommentRepository commentRepository,
|
||||||
IReadStore readStore)
|
IReadStore readStore,
|
||||||
|
ICurrentUser currentUser)
|
||||||
{
|
{
|
||||||
_repository = repository;
|
_repository = repository;
|
||||||
_commentRepository = commentRepository;
|
_commentRepository = commentRepository;
|
||||||
_readStore = readStore;
|
_readStore = readStore;
|
||||||
|
_currentUser = currentUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid PublishLink(PublishLinkCommand cmd)
|
public Guid PublishLink(PublishLinkCommand cmd)
|
||||||
{
|
{
|
||||||
var link = new Link(cmd.Url, cmd.Description);
|
var link = new Link(_currentUser.GetCurrentUserId(),
|
||||||
|
cmd.Url,
|
||||||
|
cmd.Description);
|
||||||
|
|
||||||
_repository.Add(link);
|
_repository.Add(link);
|
||||||
|
|
||||||
@ -29,7 +34,7 @@ public class LinkService
|
|||||||
|
|
||||||
public Guid PublishComment(PublishCommentCommand cmd)
|
public Guid PublishComment(PublishCommentCommand cmd)
|
||||||
{
|
{
|
||||||
var comment = new Comment(cmd.LinkId, cmd.Content);
|
var comment = new Comment(_currentUser.GetCurrentUserId(), cmd.LinkId, cmd.Content);
|
||||||
|
|
||||||
_commentRepository.Add(comment);
|
_commentRepository.Add(comment);
|
||||||
|
|
||||||
@ -53,17 +58,24 @@ public interface IReadStore
|
|||||||
LinkComment[] GetLinkComments(Guid linkId);
|
LinkComment[] GetLinkComments(Guid linkId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface ICurrentUser
|
||||||
|
{
|
||||||
|
string GetCurrentUserId();
|
||||||
|
}
|
||||||
|
|
||||||
public class LinkHomePage
|
public class LinkHomePage
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public int CommentsCount { get; set; }
|
public int CommentsCount { get; set; }
|
||||||
|
public string Author { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LinkComment
|
public class LinkComment
|
||||||
{
|
{
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
public string User { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PublishLinkCommand
|
public class PublishLinkCommand
|
||||||
|
|||||||
@ -6,12 +6,14 @@ public class Comment
|
|||||||
public Guid LinkId { get; }
|
public Guid LinkId { get; }
|
||||||
public string Content { get; }
|
public string Content { get; }
|
||||||
public DateTime CreatedAt { get; }
|
public DateTime CreatedAt { get; }
|
||||||
|
public string CreatedBy { get; }
|
||||||
|
|
||||||
public Comment(Guid linkId, string content)
|
public Comment(string createdBy, Guid linkId, string content)
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid();
|
Id = Guid.NewGuid();
|
||||||
LinkId = linkId;
|
LinkId = linkId;
|
||||||
Content = content;
|
Content = content;
|
||||||
CreatedAt = DateTime.UtcNow;
|
CreatedAt = DateTime.UtcNow;
|
||||||
|
CreatedBy = createdBy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,12 +6,14 @@ public class Link
|
|||||||
public string Url { get; }
|
public string Url { get; }
|
||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
public DateTime CreatedAt { get; }
|
public DateTime CreatedAt { get; }
|
||||||
|
public string CreatedBy { get; }
|
||||||
|
|
||||||
public Link(string url, string description)
|
public Link(string createdBy, string url, string description)
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid();
|
Id = Guid.NewGuid();
|
||||||
Url = url;
|
Url = url;
|
||||||
Description = description;
|
Description = description;
|
||||||
CreatedAt = DateTime.UtcNow;
|
CreatedAt = DateTime.UtcNow;
|
||||||
|
CreatedBy = createdBy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,8 +13,8 @@ public static class ServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
public static IServiceCollection AddHackerNetServicesMemory(this IServiceCollection services)
|
public static IServiceCollection AddHackerNetServicesMemory(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
var link = new Link("https://localhost:7050/", "Youhouuu");
|
var link = new Link("", "https://localhost:7050/", "Youhouuu");
|
||||||
var comment = new Comment(link.Id, "Wow!");
|
var comment = new Comment("", link.Id, "Wow!");
|
||||||
var linksRepository = new MemoryLinkRepository(link);
|
var linksRepository = new MemoryLinkRepository(link);
|
||||||
var commentsRepository = new MemoryCommentRepository(comment);
|
var commentsRepository = new MemoryCommentRepository(comment);
|
||||||
|
|
||||||
@ -33,6 +33,7 @@ public static class ServiceCollectionExtensions
|
|||||||
|
|
||||||
services.AddDbContext<HackerContext>(options
|
services.AddDbContext<HackerContext>(options
|
||||||
=> options.UseSqlite(configuration.GetConnectionString("Default")));
|
=> options.UseSqlite(configuration.GetConnectionString("Default")));
|
||||||
|
services.AddScoped<ICurrentUser, HttpCurrentUser>();
|
||||||
services.AddScoped<ILinkRepository, EFLinkRepository>();
|
services.AddScoped<ILinkRepository, EFLinkRepository>();
|
||||||
services.AddScoped<ICommentRepository, EFCommentRepository>();
|
services.AddScoped<ICommentRepository, EFCommentRepository>();
|
||||||
services.AddScoped<IReadStore, EFReadStore>();
|
services.AddScoped<IReadStore, EFReadStore>();
|
||||||
|
|||||||
23
HackerNet.Infrastructure/AspNet/HttpCurrentUser.cs
Normal file
23
HackerNet.Infrastructure/AspNet/HttpCurrentUser.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using HackerNet.Application;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace HackerNet.Infrastructure.AspNet;
|
||||||
|
|
||||||
|
public class HttpCurrentUser : ICurrentUser
|
||||||
|
{
|
||||||
|
private readonly IHttpContextAccessor _contextAccessor;
|
||||||
|
private readonly UserManager<IdentityUser> _userManager;
|
||||||
|
|
||||||
|
public HttpCurrentUser(IHttpContextAccessor contextAccessor, UserManager<IdentityUser> userManager)
|
||||||
|
{
|
||||||
|
_contextAccessor = contextAccessor;
|
||||||
|
_userManager = userManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetCurrentUserId()
|
||||||
|
{
|
||||||
|
var userPrincipal = _contextAccessor.HttpContext.User;
|
||||||
|
return _userManager.GetUserId(userPrincipal);
|
||||||
|
}
|
||||||
|
}
|
||||||
349
HackerNet.Infrastructure/Migrations/20211215145254_AddCreatedBy.Designer.cs
generated
Normal file
349
HackerNet.Infrastructure/Migrations/20211215145254_AddCreatedBy.Designer.cs
generated
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using HackerNet.Infrastructure.Repositories.EntityFramework;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace HackerNet.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(HackerContext))]
|
||||||
|
[Migration("20211215145254_AddCreatedBy")]
|
||||||
|
partial class AddCreatedBy
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "6.0.1");
|
||||||
|
|
||||||
|
modelBuilder.Entity("HackerNet.Domain.Comment", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Content")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("LinkId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
|
b.HasIndex("LinkId");
|
||||||
|
|
||||||
|
b.ToTable("Comments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("HackerNet.Domain.Link", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
|
b.ToTable("Links");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
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("HackerNet.Domain.Comment", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("HackerNet.Domain.Link", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("LinkId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("HackerNet.Domain.Link", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace HackerNet.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
public partial class AddCreatedBy : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("DELETE FROM Links; DELETE FROM Comments;");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "CreatedBy",
|
||||||
|
table: "Links",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "CreatedBy",
|
||||||
|
table: "Comments",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,11 +30,17 @@ namespace HackerNet.Infrastructure.Migrations
|
|||||||
b.Property<DateTime>("CreatedAt")
|
b.Property<DateTime>("CreatedAt")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<Guid>("LinkId")
|
b.Property<Guid>("LinkId")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
b.HasIndex("LinkId");
|
b.HasIndex("LinkId");
|
||||||
|
|
||||||
b.ToTable("Comments");
|
b.ToTable("Comments");
|
||||||
@ -49,6 +55,10 @@ namespace HackerNet.Infrastructure.Migrations
|
|||||||
b.Property<DateTime>("CreatedAt")
|
b.Property<DateTime>("CreatedAt")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
@ -60,6 +70,8 @@ namespace HackerNet.Infrastructure.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedBy");
|
||||||
|
|
||||||
b.ToTable("Links");
|
b.ToTable("Links");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -257,6 +269,12 @@ namespace HackerNet.Infrastructure.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("HackerNet.Domain.Comment", b =>
|
modelBuilder.Entity("HackerNet.Domain.Comment", b =>
|
||||||
{
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("HackerNet.Domain.Link", null)
|
b.HasOne("HackerNet.Domain.Link", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("LinkId")
|
.HasForeignKey("LinkId")
|
||||||
@ -264,6 +282,15 @@ namespace HackerNet.Infrastructure.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("HackerNet.Domain.Link", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedBy")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
|||||||
@ -27,16 +27,30 @@ public class EFReadStore : IReadStore
|
|||||||
|
|
||||||
private IQueryable<LinkHomePage> GetLinks(Guid? id = null)
|
private IQueryable<LinkHomePage> GetLinks(Guid? id = null)
|
||||||
{
|
{
|
||||||
return _context.Links
|
return (from link in _context.Links
|
||||||
.Where(l => !id.HasValue || l.Id == id)
|
join user in _context.Users on link.CreatedBy equals user.Id
|
||||||
.OrderByDescending(l => l.CreatedAt)
|
orderby link.CreatedAt descending
|
||||||
.Select(l => new LinkHomePage
|
select new LinkHomePage
|
||||||
{
|
{
|
||||||
Id = l.Id,
|
Id = link.Id,
|
||||||
Url = l.Url,
|
Url = link.Url,
|
||||||
Description = l.Description,
|
Description = link.Description,
|
||||||
CommentsCount = _context.Comments
|
CommentsCount = _context.Comments.Count(c => c.LinkId == link.Id),
|
||||||
.Count(c => c.LinkId == l.Id),
|
Author = user.UserName,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// return _context.Links
|
||||||
|
// .Where(l => !id.HasValue || l.Id == id)
|
||||||
|
// .OrderByDescending(l => l.CreatedAt)
|
||||||
|
// .Join()
|
||||||
|
// .Select(l => new LinkHomePage
|
||||||
|
// {
|
||||||
|
// Id = l.Id,
|
||||||
|
// Url = l.Url,
|
||||||
|
// Description = l.Description,
|
||||||
|
// CommentsCount = _context.Comments
|
||||||
|
// .Count(c => c.LinkId == l.Id),
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using HackerNet.Domain;
|
using HackerNet.Domain;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
@ -16,5 +17,10 @@ public class CommentEntityTypeConfiguration : IEntityTypeConfiguration<Comment>
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
builder.Property(o => o.Content).IsRequired();
|
builder.Property(o => o.Content).IsRequired();
|
||||||
builder.Property(o => o.CreatedAt).IsRequired();
|
builder.Property(o => o.CreatedAt).IsRequired();
|
||||||
|
builder.HasOne<IdentityUser>()
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(o => o.CreatedBy)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using HackerNet.Domain;
|
using HackerNet.Domain;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
@ -12,5 +13,11 @@ public class LinkEntityTypeConfiguration : IEntityTypeConfiguration<Link>
|
|||||||
builder.Property(o => o.Url).IsRequired().HasMaxLength(250);
|
builder.Property(o => o.Url).IsRequired().HasMaxLength(250);
|
||||||
builder.Property(o => o.Description).IsRequired();
|
builder.Property(o => o.Description).IsRequired();
|
||||||
builder.Property(o => o.CreatedAt).IsRequired();
|
builder.Property(o => o.CreatedAt).IsRequired();
|
||||||
|
|
||||||
|
builder.HasOne<IdentityUser>()
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(o => o.CreatedBy)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user