Fixed migrations

This commit is contained in:
Fergal Moran
2024-03-22 17:56:25 +00:00
parent 2fb98f5c51
commit 1e5ab38413
15 changed files with 980 additions and 218 deletions

View File

@@ -112,12 +112,10 @@ public class UserEndpoints : Shared.Endpoints {
provider = provider.Pascalize();
var allowedOrigins = configuration.GetSection("AllowedOrigins")?.Get<string[]>() ?? Array.Empty<string>();
var allowedOrigins = configuration.GetSection("AllowedOrigins").Get<string[]>() ?? Array.Empty<string>();
if (!allowedOrigins.Any(origin => Uri.Compare(
new Uri(origin, UriKind.Absolute),
new Uri(origin), UriComponents.SchemeAndServer, UriFormat.UriEscaped,
StringComparison.OrdinalIgnoreCase) == 0))
if (allowedOrigins.All(origin => Uri.Compare(new Uri(origin, UriKind.Absolute), new Uri(origin),
UriComponents.SchemeAndServer, UriFormat.UriEscaped, StringComparison.OrdinalIgnoreCase) != 0))
throw new BadRequestException(nameof(returnUrl), $"'{nameof(returnUrl)}' is not allowed.");
// Request a redirect to the external sign-in provider.

View File

@@ -8,12 +8,18 @@ using Microsoft.EntityFrameworkCore;
using TvNoms.Server.ApiService.Shared;
using TvNoms.Server.Data;
using TvNoms.Server.Data.Models;
using TvNoms.Server.Services.Data.Models;
using TvNoms.Server.Services.Data.Repositories;
using TvNoms.Server.Services.Identity;
using TvNoms.Server.Services.Infrastructure.Messaging;
using TvNoms.Server.Services.Infrastructure.Messaging.Email;
using TvNoms.Server.Services.Infrastructure.Messaging.SMS;
using TvNoms.Server.Services.Infrastructure.Storage;
using TvNoms.Server.Services.Utilities;
using TvNoms.Server.Services.ViewRenderer.Razor;
var builder = WebApplication.CreateBuilder(args);
var assemblies = AssemblyHelper.GetAssemblies().ToArray();
// Add service defaults & Aspire components.
builder.AddServiceDefaults();
@@ -45,6 +51,11 @@ builder.Services.AddDbContext<AppDbContext>(options => {
options.UseNpgsql(connectionString,
sqlOptions => sqlOptions.MigrationsAssembly(typeof(AppDbContext).Assembly.GetName().Name));
});
builder.Services.AddAutoMapper(assemblies);
builder.Services.AddMediatR(options => { options.RegisterServicesFromAssemblies(assemblies); });
builder.Services.AddRepositories(assemblies);
builder.Services.AddValidators(assemblies);
builder.Services.AddIdentity<User, Role>(options => {
// Password settings. (Will be using fluent validation)
options.Password.RequireDigit = false;
@@ -94,18 +105,26 @@ builder.Services.AddAuthentication(options => {
options.SignInScheme = IdentityConstants.ExternalScheme;
builder.Configuration.GetRequiredSection("GoogleAuthOptions").Bind(options);
});
builder.Services.AddModelBuilder();
builder.Services.AddAuthorization();
builder.Services.AddMailgunEmailSender(options => {
builder.Configuration.GetRequiredSection("MailgunEmailOptions").Bind(options);
});
builder.Services.AddFakeSmsSender();
builder.Services.AddRazorViewRenderer();
builder.Services.AddLocalFileStorage(options => {
options.RootPath = Path.Combine(builder.Environment.WebRootPath, "uploads");
options.WebRootPath = "/uploads";
});
builder.Services.AddDocumentations();
builder.Services.AddWebAppCors();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseExceptionHandler();
app.UseCors("WebAppPolicy");
app.MapGet("/ping", () => "pong");

View File

@@ -17,6 +17,10 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.3" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

View File

@@ -16,6 +16,10 @@
"GoogleAuthOptions": {
"ClientId": "547087534129-35mk786fqjgglbucjmroq9q6kgo0tvdl.apps.googleusercontent.com"
},
"AllowedHosts": "*",
"AllowedOrigins": [
"https://tvnoms.dev.fergl.ie:3000"
],
"BearerAuthOptions": {
"Secret": null,
"Issuer": null,

View File

@@ -4,16 +4,14 @@ using TvNoms.Server.Data.Models;
namespace TvNoms.Server.Data;
public class AppDbContext : DbContext {
private readonly IConfiguration _configuration;
public class AppDbContext(IConfiguration configuration) : DbContext {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
optionsBuilder.UseNpgsql(configuration.GetConnectionString("DefaultConnection"));
public AppDbContext(IConfiguration configuration) {
_configuration = configuration;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql(_configuration.GetConnectionString("DefaultConnection"));
public DbSet<User> Users { get; set; }
public DbSet<Client> Clients { get; set; }
public DbSet<Show> Shows { get; set; }
public DbSet<Movie> Movies { get; set; }
public DbSet<Media> Media { get; set; }
}

View File

@@ -1,72 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TvNoms.Server.Data;
#nullable disable
namespace TvNoms.Server.Data.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20240314203159_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TvNoms.Server.Data.Models.Movie", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Movies");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Show", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Shows");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -1,53 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TvNoms.Server.Data.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Movies",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Title = table.Column<string>(type: "text", nullable: false),
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movies", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Shows",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Title = table.Column<string>(type: "text", nullable: false),
DateCreated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
DateUpdated = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Shows", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Movies");
migrationBuilder.DropTable(
name: "Shows");
}
}
}

View File

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

View File

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

View File

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

View File

@@ -1,69 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TvNoms.Server.Data;
#nullable disable
namespace TvNoms.Server.Data.Migrations
{
[DbContext(typeof(AppDbContext))]
partial class TvNomsContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TvNoms.Server.Data.Models.Movie", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Movies");
});
modelBuilder.Entity("TvNoms.Server.Data.Models.Show", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateUpdated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Shows");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -20,26 +20,22 @@ public class User : IdentityUser<Guid>, IEntity {
}
public class UserRole : IdentityUserRole<Guid>, IEntity {
public Guid Id { get; set; }
public virtual User User { get; set; } = default!;
public virtual Role Role { get; set; } = default!;
Guid IEntity.Id { get; set; }
}
public class UserSession : IEntity {
public virtual User User { get; set; } = default!;
public Guid UserId { get; set; }
public Guid Id { get; set; }
public virtual User User { get; set; } = default!;
public Guid UserId { get; set; }
public string AccessTokenHash { get; set; } = default!;
public DateTimeOffset AccessTokenExpiresAt { get; set; }
public string RefreshTokenHash { get; set; } = default!;
public DateTimeOffset RefreshTokenExpiresAt { get; set; }
}

View File

@@ -0,0 +1,23 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using TvNoms.Server.Services.Utilities;
namespace TvNoms.Server.Services.Data.Repositories;
public static class ServiceCollectionExtensions {
public static IServiceCollection AddRepositories(this IServiceCollection services, IEnumerable<Assembly> assemblies) {
var repositoryTypes = assemblies.SelectMany(_ => _.DefinedTypes).Select(_ => _.AsType())
.Where(type => type is { IsClass: true, IsAbstract: false } && type.IsCompatibleWith(typeof(IRepository<>)));
foreach (var concreteType in repositoryTypes) {
var matchingInterfaceType = concreteType.GetInterfaces()
.FirstOrDefault(x => string.Equals(x.Name, $"I{concreteType.Name}", StringComparison.Ordinal));
if (matchingInterfaceType != null) {
services.AddScoped(matchingInterfaceType, concreteType);
}
}
return services;
}
}

View File

@@ -8,6 +8,19 @@ using TvNoms.Server.Services.Data.Services;
namespace TvNoms.Server.Services.Identity;
public static class ServiceCollectionExtensions {
public static IServiceCollection AddWebAppCors(this IServiceCollection services) {
services.AddCors(options => {
options.AddPolicy("WebAppPolicy", builder => {
builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.WithOrigins("https://tvnoms.dev.fergl.ie:3000/");
});
});
return services;
}
public static AuthenticationBuilder AddBearer(this AuthenticationBuilder builder,
Action<UserSessionOptions> options) {
builder.Services.AddOptions<UserSessionOptions>().Configure<IHttpContextAccessor>(

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
rm -rfv ./TvNoms.Data/Migrations
dotnet ef database drop --force --startup-project ./TvNoms.ApiService/TvNoms.ApiService.csproj --project ./TvNoms.Data/TvNoms.Data.csproj
dotnet ef migrations add "Initial" --startup-project ./TvNoms.ApiService/TvNoms.ApiService.csproj --project ./TvNoms.Data/TvNoms.Data.csproj
dotnet ef database update --startup-project ./TvNoms.ApiService/TvNoms.ApiService.csproj --project ./TvNoms.Data/TvNoms.Data.csproj