diff --git a/Jellyfin.Data/Entities/Security/Device.cs b/Jellyfin.Data/Entities/Security/Device.cs
index 3d0269229d..9490323b19 100644
--- a/Jellyfin.Data/Entities/Security/Device.cs
+++ b/Jellyfin.Data/Entities/Security/Device.cs
@@ -2,13 +2,14 @@ using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
+using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Security
{
///
/// An entity representing a device.
///
- public class Device
+ public class Device : IAuditableEntity
{
///
/// Initializes a new instance of the class.
@@ -28,6 +29,7 @@ namespace Jellyfin.Data.Entities.Security
AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
DateCreated = DateTime.UtcNow;
+ DateModified = DateCreated;
DateLastActivity = DateCreated;
// Non-nullable for EF Core, as this is a required relationship.
@@ -83,11 +85,12 @@ namespace Jellyfin.Data.Entities.Security
///
public bool IsActive { get; set; }
- ///
- /// Gets or sets the date this device was created.
- ///
+ ///
public DateTime DateCreated { get; set; }
+ ///
+ public DateTime DateModified { get; set; }
+
///
/// Gets or sets the date of last activity.
///
diff --git a/Jellyfin.Data/Interfaces/IAuditableEntity.cs b/Jellyfin.Data/Interfaces/IAuditableEntity.cs
new file mode 100644
index 0000000000..4420446ae6
--- /dev/null
+++ b/Jellyfin.Data/Interfaces/IAuditableEntity.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Jellyfin.Data.Interfaces
+{
+ ///
+ /// An interface representing an entity that has creation/modification dates.
+ ///
+ public interface IAuditableEntity
+ {
+ ///
+ /// Gets the date this entity was created.
+ ///
+ public DateTime DateCreated { get; }
+
+ ///
+ /// Gets or sets the date this entity was modified.
+ ///
+ public DateTime DateModified { get; set; }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/20210602224232_AddDevices.cs b/Jellyfin.Server.Implementations/Migrations/20210602224232_AddDevices.cs
deleted file mode 100644
index 110ca1cc39..0000000000
--- a/Jellyfin.Server.Implementations/Migrations/20210602224232_AddDevices.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1601
-
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Jellyfin.Server.Implementations.Migrations
-{
- public partial class AddDevices : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "ApiKeys",
- schema: "jellyfin",
- columns: table => new
- {
- Id = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- DateCreated = table.Column(type: "TEXT", nullable: false),
- DateLastActivity = table.Column(type: "TEXT", nullable: false),
- Name = table.Column(type: "TEXT", maxLength: 64, nullable: false),
- AccessToken = table.Column(type: "TEXT", nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_ApiKeys", x => x.Id);
- });
-
- migrationBuilder.CreateTable(
- name: "DeviceOptions",
- schema: "jellyfin",
- columns: table => new
- {
- Id = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- DeviceId = table.Column(type: "TEXT", nullable: false),
- CustomName = table.Column(type: "TEXT", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_DeviceOptions", x => x.Id);
- });
-
- migrationBuilder.CreateTable(
- name: "Devices",
- schema: "jellyfin",
- columns: table => new
- {
- Id = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- UserId = table.Column(type: "TEXT", nullable: false),
- AccessToken = table.Column(type: "TEXT", nullable: false),
- AppName = table.Column(type: "TEXT", maxLength: 64, nullable: false),
- AppVersion = table.Column(type: "TEXT", maxLength: 32, nullable: false),
- DeviceName = table.Column(type: "TEXT", maxLength: 64, nullable: false),
- DeviceId = table.Column(type: "TEXT", maxLength: 256, nullable: false),
- IsActive = table.Column(type: "INTEGER", nullable: false),
- DateCreated = table.Column(type: "TEXT", nullable: false),
- DateLastActivity = table.Column(type: "TEXT", nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_Devices", x => x.Id);
- table.ForeignKey(
- name: "FK_Devices_Users_UserId",
- column: x => x.UserId,
- principalSchema: "jellyfin",
- principalTable: "Users",
- principalColumn: "Id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateIndex(
- name: "IX_ApiKeys_AccessToken",
- schema: "jellyfin",
- table: "ApiKeys",
- column: "AccessToken",
- unique: true);
-
- migrationBuilder.CreateIndex(
- name: "IX_DeviceOptions_DeviceId",
- schema: "jellyfin",
- table: "DeviceOptions",
- column: "DeviceId",
- unique: true);
-
- migrationBuilder.CreateIndex(
- name: "IX_Devices_AccessToken_DateLastActivity",
- schema: "jellyfin",
- table: "Devices",
- columns: new[] { "AccessToken", "DateLastActivity" });
-
- migrationBuilder.CreateIndex(
- name: "IX_Devices_DeviceId",
- schema: "jellyfin",
- table: "Devices",
- column: "DeviceId");
-
- migrationBuilder.CreateIndex(
- name: "IX_Devices_DeviceId_DateLastActivity",
- schema: "jellyfin",
- table: "Devices",
- columns: new[] { "DeviceId", "DateLastActivity" });
-
- migrationBuilder.CreateIndex(
- name: "IX_Devices_UserId_DeviceId",
- schema: "jellyfin",
- table: "Devices",
- columns: new[] { "UserId", "DeviceId" });
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "ApiKeys",
- schema: "jellyfin");
-
- migrationBuilder.DropTable(
- name: "DeviceOptions",
- schema: "jellyfin");
-
- migrationBuilder.DropTable(
- name: "Devices",
- schema: "jellyfin");
- }
- }
-}
diff --git a/Jellyfin.Server.Implementations/Migrations/20210602224232_AddDevices.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs
similarity index 98%
rename from Jellyfin.Server.Implementations/Migrations/20210602224232_AddDevices.Designer.cs
rename to Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs
index 933e82229d..7e9566e2ea 100644
--- a/Jellyfin.Server.Implementations/Migrations/20210602224232_AddDevices.Designer.cs
+++ b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs
@@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Jellyfin.Server.Implementations.Migrations
{
[DbContext(typeof(JellyfinDb))]
- [Migration("20210602224232_AddDevices")]
+ [Migration("20210814002109_AddDevices")]
partial class AddDevices
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -19,7 +19,7 @@ namespace Jellyfin.Server.Implementations.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("jellyfin")
- .HasAnnotation("ProductVersion", "5.0.6");
+ .HasAnnotation("ProductVersion", "5.0.7");
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
{
@@ -342,7 +342,8 @@ namespace Jellyfin.Server.Implementations.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
- b.Property("AccessToken")
+ b.Property("AccessToken")
+ .IsRequired()
.HasColumnType("TEXT");
b.Property("DateCreated")
@@ -390,6 +391,9 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Property("DateLastActivity")
.HasColumnType("TEXT");
+ b.Property("DateModified")
+ .HasColumnType("TEXT");
+
b.Property("DeviceId")
.IsRequired()
.HasMaxLength(256)
diff --git a/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.cs b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.cs
new file mode 100644
index 0000000000..81bf890075
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.cs
@@ -0,0 +1,348 @@
+#pragma warning disable CS1591, SA1601
+
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+ public partial class AddDevices : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_ImageInfos_Users_UserId",
+ schema: "jellyfin",
+ table: "ImageInfos");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Permissions_Users_Permission_Permissions_Guid",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Preferences_Users_Preference_Preferences_Guid",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Preferences_Preference_Preferences_Guid",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Permissions_Permission_Permissions_Guid",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.DropIndex(
+ name: "IX_DisplayPreferences_UserId",
+ schema: "jellyfin",
+ table: "DisplayPreferences");
+
+ migrationBuilder.DropIndex(
+ name: "IX_CustomItemDisplayPreferences_UserId",
+ schema: "jellyfin",
+ table: "CustomItemDisplayPreferences");
+
+ migrationBuilder.AlterColumn(
+ name: "Username",
+ schema: "jellyfin",
+ table: "Users",
+ type: "TEXT",
+ maxLength: 255,
+ nullable: false,
+ collation: "NOCASE",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 255);
+
+ migrationBuilder.AddColumn(
+ name: "UserId",
+ schema: "jellyfin",
+ table: "Preferences",
+ type: "TEXT",
+ nullable: true);
+
+ migrationBuilder.AddColumn(
+ name: "UserId",
+ schema: "jellyfin",
+ table: "Permissions",
+ type: "TEXT",
+ nullable: true);
+
+ migrationBuilder.CreateTable(
+ name: "ApiKeys",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ DateCreated = table.Column(type: "TEXT", nullable: false),
+ DateLastActivity = table.Column(type: "TEXT", nullable: false),
+ Name = table.Column(type: "TEXT", maxLength: 64, nullable: false),
+ AccessToken = table.Column(type: "TEXT", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ApiKeys", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "DeviceOptions",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ DeviceId = table.Column(type: "TEXT", nullable: false),
+ CustomName = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_DeviceOptions", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Devices",
+ schema: "jellyfin",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ UserId = table.Column(type: "TEXT", nullable: false),
+ AccessToken = table.Column(type: "TEXT", nullable: false),
+ AppName = table.Column(type: "TEXT", maxLength: 64, nullable: false),
+ AppVersion = table.Column(type: "TEXT", maxLength: 32, nullable: false),
+ DeviceName = table.Column(type: "TEXT", maxLength: 64, nullable: false),
+ DeviceId = table.Column(type: "TEXT", maxLength: 256, nullable: false),
+ IsActive = table.Column(type: "INTEGER", nullable: false),
+ DateCreated = table.Column(type: "TEXT", nullable: false),
+ DateModified = table.Column(type: "TEXT", nullable: false),
+ DateLastActivity = table.Column(type: "TEXT", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Devices", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Devices_Users_UserId",
+ column: x => x.UserId,
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Users_Username",
+ schema: "jellyfin",
+ table: "Users",
+ column: "Username",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Preferences_UserId_Kind",
+ schema: "jellyfin",
+ table: "Preferences",
+ columns: new[] { "UserId", "Kind" },
+ unique: true,
+ filter: "[UserId] IS NOT NULL");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Permissions_UserId_Kind",
+ schema: "jellyfin",
+ table: "Permissions",
+ columns: new[] { "UserId", "Kind" },
+ unique: true,
+ filter: "[UserId] IS NOT NULL");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ApiKeys_AccessToken",
+ schema: "jellyfin",
+ table: "ApiKeys",
+ column: "AccessToken",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_DeviceOptions_DeviceId",
+ schema: "jellyfin",
+ table: "DeviceOptions",
+ column: "DeviceId",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Devices_AccessToken_DateLastActivity",
+ schema: "jellyfin",
+ table: "Devices",
+ columns: new[] { "AccessToken", "DateLastActivity" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Devices_DeviceId",
+ schema: "jellyfin",
+ table: "Devices",
+ column: "DeviceId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Devices_DeviceId_DateLastActivity",
+ schema: "jellyfin",
+ table: "Devices",
+ columns: new[] { "DeviceId", "DateLastActivity" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Devices_UserId_DeviceId",
+ schema: "jellyfin",
+ table: "Devices",
+ columns: new[] { "UserId", "DeviceId" });
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_ImageInfos_Users_UserId",
+ schema: "jellyfin",
+ table: "ImageInfos",
+ column: "UserId",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Permissions_Users_UserId",
+ schema: "jellyfin",
+ table: "Permissions",
+ column: "UserId",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Preferences_Users_UserId",
+ schema: "jellyfin",
+ table: "Preferences",
+ column: "UserId",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_ImageInfos_Users_UserId",
+ schema: "jellyfin",
+ table: "ImageInfos");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Permissions_Users_UserId",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_Preferences_Users_UserId",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropTable(
+ name: "ApiKeys",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "DeviceOptions",
+ schema: "jellyfin");
+
+ migrationBuilder.DropTable(
+ name: "Devices",
+ schema: "jellyfin");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Users_Username",
+ schema: "jellyfin",
+ table: "Users");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Preferences_UserId_Kind",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropIndex(
+ name: "IX_Permissions_UserId_Kind",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.DropColumn(
+ name: "UserId",
+ schema: "jellyfin",
+ table: "Preferences");
+
+ migrationBuilder.DropColumn(
+ name: "UserId",
+ schema: "jellyfin",
+ table: "Permissions");
+
+ migrationBuilder.AlterColumn(
+ name: "Username",
+ schema: "jellyfin",
+ table: "Users",
+ type: "TEXT",
+ maxLength: 255,
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 255,
+ oldCollation: "NOCASE");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Preferences_Preference_Preferences_Guid",
+ schema: "jellyfin",
+ table: "Preferences",
+ column: "Preference_Preferences_Guid");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Permissions_Permission_Permissions_Guid",
+ schema: "jellyfin",
+ table: "Permissions",
+ column: "Permission_Permissions_Guid");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_DisplayPreferences_UserId",
+ schema: "jellyfin",
+ table: "DisplayPreferences",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_CustomItemDisplayPreferences_UserId",
+ schema: "jellyfin",
+ table: "CustomItemDisplayPreferences",
+ column: "UserId");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_ImageInfos_Users_UserId",
+ schema: "jellyfin",
+ table: "ImageInfos",
+ column: "UserId",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Permissions_Users_Permission_Permissions_Guid",
+ schema: "jellyfin",
+ table: "Permissions",
+ column: "Permission_Permissions_Guid",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_Preferences_Users_Preference_Preferences_Guid",
+ schema: "jellyfin",
+ table: "Preferences",
+ column: "Preference_Preferences_Guid",
+ principalSchema: "jellyfin",
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
index b508b834ce..fcc360e260 100644
--- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
+++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
@@ -15,7 +15,7 @@ namespace Jellyfin.Server.Implementations.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("jellyfin")
- .HasAnnotation("ProductVersion", "5.0.6");
+ .HasAnnotation("ProductVersion", "5.0.7");
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
{
@@ -338,7 +338,8 @@ namespace Jellyfin.Server.Implementations.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
- b.Property("AccessToken")
+ b.Property("AccessToken")
+ .IsRequired()
.HasColumnType("TEXT");
b.Property("DateCreated")
@@ -386,6 +387,9 @@ namespace Jellyfin.Server.Implementations.Migrations
b.Property("DateLastActivity")
.HasColumnType("TEXT");
+ b.Property("DateModified")
+ .HasColumnType("TEXT");
+
b.Property("DeviceId")
.IsRequired()
.HasMaxLength(256)