Implemented Article Domain Model
This commit is contained in:
parent
0f006a7159
commit
c96b6c5ba2
43
Wave/Components/Pages/ArticleView.razor
Normal file
43
Wave/Components/Pages/ArticleView.razor
Normal file
|
@ -0,0 +1,43 @@
|
|||
@page "/article/{id:guid}"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Wave.Data
|
||||
|
||||
@inject IDbContextFactory<ApplicationDbContext> ContextFactory;
|
||||
|
||||
<PageTitle>Wave - @Article.Title</PageTitle>
|
||||
|
||||
<h1 class="text-3xl lg:text-5xl font-light mb-6">@Article.Title</h1>
|
||||
<p class="text-sm mb-3">By @Article.Author.Name</p>
|
||||
@if (Article.Reviewer is not null && Article.Reviewer.Id != Article.Author.Id) {
|
||||
<p class="text-sm mb-3">Reviewed by @Article.Reviewer.Name</p>
|
||||
}
|
||||
|
||||
<div class="prose">
|
||||
@Content
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
private Article Article { get; set; } = null!;
|
||||
private MarkupString Content => new(Article.BodyHtml);
|
||||
|
||||
protected override async Task OnInitializedAsync() {
|
||||
// We need blocking calls here, bc otherwise Blazor will execute Render in parallel,
|
||||
// running into a null pointer on the Article property and panicking
|
||||
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
await using var context = ContextFactory.CreateDbContext();
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
Article = context.Set<Article>()
|
||||
.Include(a => a.Author)
|
||||
.Include(a => a.Reviewer)
|
||||
.Where(a => a.Status >= ArticleStatus.Published && a.PublishDate <= now)
|
||||
.First(a => a.Id == Id);
|
||||
|
||||
if (Article is null) throw new ApplicationException("Article not found.");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
@page "/"
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Wave.Data
|
||||
|
||||
@rendermode InteractiveServer
|
||||
@attribute [StreamRendering]
|
||||
@inject IDbContextFactory<ApplicationDbContext> ContextFactory;
|
||||
@inject IStringLocalizer<Home> Localizer
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
@ -8,3 +13,29 @@
|
|||
<h1>@Localizer["Greeting"]</h1>
|
||||
|
||||
Welcome to your new app.
|
||||
|
||||
<div>
|
||||
@foreach (Article article in Articles) {
|
||||
<div>
|
||||
<a href="/article/@article.Id">
|
||||
<p>@article.Title</p>
|
||||
<p>By @article.Author.Name</p>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private List<Article> Articles { get; } = [];
|
||||
|
||||
protected override async Task OnInitializedAsync() {
|
||||
await using var context = await ContextFactory.CreateDbContextAsync();
|
||||
|
||||
var articles = await context.Set<Article>()
|
||||
.Include(a => a.Author)
|
||||
.OrderBy(a => a.PublishDate)
|
||||
.Take(10)
|
||||
.ToListAsync();
|
||||
Articles.AddRange(articles);
|
||||
}
|
||||
}
|
|
@ -20,5 +20,23 @@ public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options
|
|||
pfp.Property(p => p.ImageId).IsRequired();
|
||||
pfp.ToTable("ProfilePictures");
|
||||
});
|
||||
|
||||
builder.Entity<Article>(article => {
|
||||
article.HasKey(a => a.Id);
|
||||
article.Property(a => a.Title)
|
||||
.IsRequired().HasMaxLength(256);
|
||||
|
||||
article.HasOne(a => a.Author).WithMany()
|
||||
.IsRequired().OnDelete(DeleteBehavior.Cascade);
|
||||
article.HasOne(a => a.Reviewer).WithMany()
|
||||
.IsRequired(false).OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
article.Property(a => a.CreationDate)
|
||||
.IsRequired().HasDefaultValueSql("now()");
|
||||
article.Property(a => a.LastModified)
|
||||
.IsRequired().HasDefaultValueSql("now()");
|
||||
|
||||
article.HasQueryFilter(a => !a.IsDeleted);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,4 +4,6 @@ namespace Wave.Data;
|
|||
|
||||
public class ApplicationUser : IdentityUser {
|
||||
public ProfilePicture? ProfilePicture { get; set; }
|
||||
|
||||
public string Name => UserName ?? Email ?? Id;
|
||||
}
|
32
Wave/Data/Article.cs
Normal file
32
Wave/Data/Article.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Wave.Data;
|
||||
|
||||
public enum ArticleStatus {
|
||||
Draft = 0,
|
||||
InReview = 1,
|
||||
Published = 2
|
||||
}
|
||||
|
||||
// TODO:: Add category / tags for MVP
|
||||
// TODO:: Archive System (Notice / Redirect to new content?) (Deprecation date?)
|
||||
// TODO:: Reference used files?
|
||||
|
||||
public class Article : ISoftDelete {
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
[MaxLength(256)]
|
||||
public required string Title { get; set; }
|
||||
public required string Body { get; set; }
|
||||
public string BodyHtml { get; set; } = string.Empty;
|
||||
|
||||
public required ApplicationUser Author { get; set; }
|
||||
public ApplicationUser? Reviewer { get; set; }
|
||||
|
||||
public ArticleStatus Status { get; set; }
|
||||
public DateTimeOffset CreationDate { get; set; } = DateTimeOffset.Now;
|
||||
public DateTimeOffset PublishDate { get; set; } = DateTimeOffset.MaxValue;
|
||||
public DateTimeOffset? LastModified { get; set; }
|
||||
}
|
5
Wave/Data/ISoftDelete.cs
Normal file
5
Wave/Data/ISoftDelete.cs
Normal file
|
@ -0,0 +1,5 @@
|
|||
namespace Wave.Data;
|
||||
|
||||
public interface ISoftDelete {
|
||||
bool IsDeleted { get; set; }
|
||||
}
|
385
Wave/Data/Migrations/postgres/20240115181000_Articles.Designer.cs
generated
Normal file
385
Wave/Data/Migrations/postgres/20240115181000_Articles.Designer.cs
generated
Normal file
|
@ -0,0 +1,385 @@
|
|||
// <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 Wave.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Wave.Data.Migrations.postgres
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20240115181000_Articles")]
|
||||
partial class Articles
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
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("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("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", 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<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", 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<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("Wave.Data.ApplicationUser", 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("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
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<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("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Wave.Data.Article", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("AuthorId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Body")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("BodyHtml")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreationDate")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastModified")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<DateTimeOffset>("PublishDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ReviewerId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorId");
|
||||
|
||||
b.HasIndex("ReviewerId");
|
||||
|
||||
b.ToTable("Article");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Wave.Data.ProfilePicture", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ApplicationUserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("ImageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApplicationUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ProfilePictures", (string)null);
|
||||
});
|
||||
|
||||
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("Wave.Data.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Wave.Data.ApplicationUser", 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("Wave.Data.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Wave.Data.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Wave.Data.Article", b =>
|
||||
{
|
||||
b.HasOne("Wave.Data.ApplicationUser", "Author")
|
||||
.WithMany()
|
||||
.HasForeignKey("AuthorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Wave.Data.ApplicationUser", "Reviewer")
|
||||
.WithMany()
|
||||
.HasForeignKey("ReviewerId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Author");
|
||||
|
||||
b.Navigation("Reviewer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Wave.Data.ProfilePicture", b =>
|
||||
{
|
||||
b.HasOne("Wave.Data.ApplicationUser", null)
|
||||
.WithOne("ProfilePicture")
|
||||
.HasForeignKey("Wave.Data.ProfilePicture", "ApplicationUserId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Wave.Data.ApplicationUser", b =>
|
||||
{
|
||||
b.Navigation("ProfilePicture");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
63
Wave/Data/Migrations/postgres/20240115181000_Articles.cs
Normal file
63
Wave/Data/Migrations/postgres/20240115181000_Articles.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Wave.Data.Migrations.postgres;
|
||||
|
||||
// Add-Migration Articles -OutputDir Data/Migrations/postgres -Project Wave -StartupProject Wave -Context ApplicationDbContext -Args "ConnectionStrings:DefaultConnection=Host=localhost;Port=5432;AllowAnonymousConnections=true"
|
||||
/// <inheritdoc />
|
||||
public partial class Articles : Migration {
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) {
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Article",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
IsDeleted = table.Column<bool>(type: "boolean", nullable: false),
|
||||
Title = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||
Body = table.Column<string>(type: "text", nullable: false),
|
||||
BodyHtml = table.Column<string>(type: "text", nullable: false),
|
||||
AuthorId = table.Column<string>(type: "text", nullable: false),
|
||||
ReviewerId = table.Column<string>(type: "text", nullable: true),
|
||||
Status = table.Column<int>(type: "integer", nullable: false),
|
||||
CreationDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false,
|
||||
defaultValueSql: "now()"),
|
||||
PublishDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
LastModified = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false,
|
||||
defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table => {
|
||||
table.PrimaryKey("PK_Article", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Article_AspNetUsers_AuthorId",
|
||||
column: x => x.AuthorId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Article_AspNetUsers_ReviewerId",
|
||||
column: x => x.ReviewerId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Article_AuthorId",
|
||||
table: "Article",
|
||||
column: "AuthorId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Article_ReviewerId",
|
||||
table: "Article",
|
||||
column: "ReviewerId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) {
|
||||
migrationBuilder.DropTable(
|
||||
name: "Article");
|
||||
}
|
||||
}
|
|
@ -218,6 +218,61 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Wave.Data.Article", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("AuthorId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Body")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("BodyHtml")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreationDate")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastModified")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<DateTimeOffset>("PublishDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ReviewerId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorId");
|
||||
|
||||
b.HasIndex("ReviewerId");
|
||||
|
||||
b.ToTable("Article");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Wave.Data.ProfilePicture", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
@ -291,6 +346,24 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Wave.Data.Article", b =>
|
||||
{
|
||||
b.HasOne("Wave.Data.ApplicationUser", "Author")
|
||||
.WithMany()
|
||||
.HasForeignKey("AuthorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Wave.Data.ApplicationUser", "Reviewer")
|
||||
.WithMany()
|
||||
.HasForeignKey("ReviewerId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Author");
|
||||
|
||||
b.Navigation("Reviewer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Wave.Data.ProfilePicture", b =>
|
||||
{
|
||||
b.HasOne("Wave.Data.ApplicationUser", null)
|
||||
|
|
|
@ -84,4 +84,10 @@
|
|||
.AddSupportedCultures(cultures)
|
||||
.AddSupportedUICultures(cultures));
|
||||
|
||||
{
|
||||
using var scope = app.Services.CreateScope();
|
||||
using var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
context.Database.Migrate();
|
||||
}
|
||||
|
||||
app.Run();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.5.0" />
|
||||
<PackageReference Include="Markdig" Version="0.34.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
|
||||
|
|
2
Wave/wwwroot/css/main.min.css
vendored
2
Wave/wwwroot/css/main.min.css
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue