Ajout de l'authentification (compte unique, JWT, reset par email)
Application mono-utilisateur : inscription unique, connexion par JWT (12 h), protection de toutes les routes de données, réinitialisation de mot de passe par email avec repli de journalisation, onboarding réorganisé en deux étapes (inscription → connexion → profil + véhicule). Backend (SchotzIadKmTracker.Api) : - Entités Compte (singleton) et ResetPasswordToken + migration AjoutAuthentification. - Hachage PasswordHasher, JwtService (HS256, durée configurable), IEmailService (MailKit + repli LoggingEmailService selon Smtp:Host). - AuthService (register 409 si compte existant, login 401 générique, forgot-password non-divulgation, reset à usage unique + expiration), AuthController (5 endpoints publics), DTOs + validateurs FluentValidation. - Sécurité pipeline : JwtBearer + filtre d'autorisation global, UseAuthentication avant UseAuthorization ; /health et /api/auth/* publics. Frontend (schotziad-km-tracker, zoneless + signals) : - AuthService (localStorage), authInterceptor (Bearer + purge/redirection 401), authGuard, écrans Login/Register/ForgotPassword/ResetPassword, routes publiques et protection [authGuard, profilConfigureGuard]. Configuration : .env.example et docker-compose.yml (JWT__SecretKey, durées, Smtp__*). Tests : 54 tests backend (propriétés FsCheck P1–P14, exemples, intégration WebApplicationFactory) + tests frontend (AuthService, authGuard, authInterceptor, ResetPasswordComponent). Suite complète au vert. Spec : .kiro/specs/authentification (requirements, design, tasks).
This commit is contained in:
@@ -20,6 +20,8 @@ public class AppDbContext : DbContext
|
||||
public DbSet<AdresseNommee> AdressesNommees => Set<AdresseNommee>();
|
||||
public DbSet<TrancheBareme> TranchesBareme => Set<TrancheBareme>();
|
||||
public DbSet<MontantRegle> MontantsRegles => Set<MontantRegle>();
|
||||
public DbSet<Compte> Comptes => Set<Compte>();
|
||||
public DbSet<ResetPasswordToken> ResetPasswordTokens => Set<ResetPasswordToken>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -110,5 +112,22 @@ public class AppDbContext : DbContext
|
||||
.HasForeignKey(m => m.VehiculeId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
// --- Comptes (singleton Id = 1) ---
|
||||
modelBuilder.Entity<Compte>(entity =>
|
||||
{
|
||||
entity.ToTable("Comptes");
|
||||
entity.HasKey(c => c.Id);
|
||||
entity.Property(c => c.Id).ValueGeneratedNever();
|
||||
entity.HasIndex(c => c.Email, "UX_Comptes_Email").IsUnique();
|
||||
});
|
||||
|
||||
// --- ResetPasswordTokens (jetons de réinitialisation à usage unique) ---
|
||||
modelBuilder.Entity<ResetPasswordToken>(entity =>
|
||||
{
|
||||
entity.ToTable("ResetPasswordTokens");
|
||||
entity.HasIndex(t => t.TokenHash, "UX_ResetTokens_TokenHash").IsUnique();
|
||||
entity.HasIndex(t => t.Expiration, "IX_ResetTokens_Expiration");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+390
@@ -0,0 +1,390 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SchotzIadKmTracker.Api.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SchotzIadKmTracker.Api.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20260914064431_AjoutAuthentification")]
|
||||
partial class AjoutAuthentification
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.AdresseCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Adresse")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("DerniereUtilisation")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("GeometrieJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<double>("Latitude")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("Longitude")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Adresse")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("AdresseCache", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.AdresseNommee", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Adresse")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Denomination")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DenominationNormalisee")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("GeocodageReussi")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double?>("Latitude")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double?>("Longitude")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex(new[] { "DenominationNormalisee" }, "UX_AdresseNommee_Denomination")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("AdressesNommees", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.Compte", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MotDePasseHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex(new[] { "Email" }, "UX_Comptes_Email")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Comptes", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.MontantRegle", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Annee")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Mois")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("Montant")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("VehiculeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("VehiculeId");
|
||||
|
||||
b.HasIndex(new[] { "Annee", "Mois", "VehiculeId" }, "UX_MontantsRegles_Periode_Vehicule")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MontantsRegles", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("CK_MontantsRegles_Mois", "\"Mois\" BETWEEN 1 AND 12");
|
||||
|
||||
t.HasCheckConstraint("CK_MontantsRegles_Montant", "\"Montant\" >= 0");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.Profil", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AdresseDomicile")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DomicileGeocode")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double?>("DomicileLatitude")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double?>("DomicileLongitude")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<string>("Matricule")
|
||||
.HasMaxLength(8)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Nom")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Prenom")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex(new[] { "Id" }, "UX_Profil_Singleton")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Profil", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.ResetPasswordToken", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Expiration")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("UsedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex(new[] { "Expiration" }, "IX_ResetTokens_Expiration");
|
||||
|
||||
b.HasIndex(new[] { "TokenHash" }, "UX_ResetTokens_TokenHash")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ResetPasswordTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.Trajet", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AdresseArrivee")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AdresseDepart")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Commentaire")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal?>("Distance")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("GeometrieJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal>("Position")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PuissanceFiscale")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("VehiculeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex(new[] { "Date" }, "IX_Trajets_Date");
|
||||
|
||||
b.HasIndex(new[] { "Date", "Position" }, "IX_Trajets_Date_Position");
|
||||
|
||||
b.HasIndex(new[] { "VehiculeId" }, "IX_Trajets_Vehicule");
|
||||
|
||||
b.ToTable("Trajets", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("CK_Trajets_Distance", "\"Distance\" IS NULL OR \"Distance\" > 0");
|
||||
|
||||
t.HasCheckConstraint("CK_Trajets_PuissanceFiscale", "\"PuissanceFiscale\" > 0");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.TrancheBareme", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Annee")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("Base")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal>("Coefficient")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal?>("KmMax")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal>("KmMin")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PuissanceFiscale")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex(new[] { "Annee", "PuissanceFiscale" }, "IX_TranchesBareme_Annee_PF");
|
||||
|
||||
b.ToTable("TranchesBareme", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("CK_TranchesBareme_Km", "\"KmMax\" IS NULL OR CAST(\"KmMin\" AS REAL) < CAST(\"KmMax\" AS REAL)");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.Vehicule", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateOnly>("DateEffet")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Denomination")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Plaque")
|
||||
.IsRequired()
|
||||
.HasMaxLength(15)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PuissanceFiscale")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex(new[] { "DateEffet" }, "IX_Vehicules_DateEffet");
|
||||
|
||||
b.ToTable("Vehicules", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("CK_Vehicules_PuissanceFiscale", "\"PuissanceFiscale\" > 0");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.MontantRegle", b =>
|
||||
{
|
||||
b.HasOne("SchotzIadKmTracker.Api.Models.Entities.Vehicule", "Vehicule")
|
||||
.WithMany()
|
||||
.HasForeignKey("VehiculeId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Vehicule");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.Trajet", b =>
|
||||
{
|
||||
b.HasOne("SchotzIadKmTracker.Api.Models.Entities.Vehicule", "Vehicule")
|
||||
.WithMany("Trajets")
|
||||
.HasForeignKey("VehiculeId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Vehicule");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.Vehicule", b =>
|
||||
{
|
||||
b.Navigation("Trajets");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SchotzIadKmTracker.Api.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AjoutAuthentification : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Comptes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Email = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
|
||||
MotDePasseHash = table.Column<string>(type: "TEXT", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Comptes", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ResetPasswordTokens",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
TokenHash = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Expiration = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
UsedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ResetPasswordTokens", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UX_Comptes_Email",
|
||||
table: "Comptes",
|
||||
column: "Email",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ResetTokens_Expiration",
|
||||
table: "ResetPasswordTokens",
|
||||
column: "Expiration");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UX_ResetTokens_TokenHash",
|
||||
table: "ResetPasswordTokens",
|
||||
column: "TokenHash",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Comptes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ResetPasswordTokens");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,34 @@ namespace SchotzIadKmTracker.Api.Data.Migrations
|
||||
b.ToTable("AdressesNommees", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.Compte", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MotDePasseHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex(new[] { "Email" }, "UX_Comptes_Email")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Comptes", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.MontantRegle", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -169,6 +197,35 @@ namespace SchotzIadKmTracker.Api.Data.Migrations
|
||||
b.ToTable("Profil", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.ResetPasswordToken", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Expiration")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("UsedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex(new[] { "Expiration" }, "IX_ResetTokens_Expiration");
|
||||
|
||||
b.HasIndex(new[] { "TokenHash" }, "UX_ResetTokens_TokenHash")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ResetPasswordTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SchotzIadKmTracker.Api.Models.Entities.Trajet", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
||||
Reference in New Issue
Block a user