diff --git a/Common/Migrations/20241122214013_Fix Petrainer998DR RFIDs.Designer.cs b/Common/Migrations/20241122214013_Fix Petrainer998DR RFIDs.Designer.cs
new file mode 100644
index 00000000..66b58cae
--- /dev/null
+++ b/Common/Migrations/20241122214013_Fix Petrainer998DR RFIDs.Designer.cs
@@ -0,0 +1,1129 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using OpenShock.Common.OpenShockDb;
+
+#nullable disable
+
+namespace OpenShock.Common.Migrations
+{
+ [DbContext(typeof(OpenShockContext))]
+ [Migration("20241122214013_Fix Petrainer998DR RFIDs")]
+ partial class FixPetrainer998DRRFIDs
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False")
+ .HasAnnotation("ProductVersion", "9.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_type", new[] { "sound", "vibrate", "shock", "stop" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "ota_update_status", new[] { "started", "running", "finished", "error", "timeout" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "password_encryption_type", new[] { "pbkdf2", "bcrypt_enhanced" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "permission_type", new[] { "shockers.use", "shockers.edit", "shockers.pause", "devices.edit", "devices.auth" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "rank_type", new[] { "user", "support", "staff", "admin", "system" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "shocker_model_type", new[] { "caiXianlin", "petTrainer", "petrainer998DR" });
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.AdminUsersView", b =>
+ {
+ b.Property("ApiTokenCount")
+ .HasColumnType("integer")
+ .HasColumnName("api_token_count");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("DeviceCount")
+ .HasColumnType("integer")
+ .HasColumnName("device_count");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("character varying")
+ .HasColumnName("email");
+
+ b.Property("EmailActivated")
+ .HasColumnType("boolean")
+ .HasColumnName("email_actived");
+
+ b.Property("EmailChangeRequestCount")
+ .HasColumnType("integer")
+ .HasColumnName("email_change_request_count");
+
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("character varying")
+ .HasColumnName("name");
+
+ b.Property("NameChangeRequestCount")
+ .HasColumnType("integer")
+ .HasColumnName("name_change_request_count");
+
+ b.Property("PasswordHashType")
+ .IsRequired()
+ .HasColumnType("character varying")
+ .HasColumnName("password_hash_type");
+
+ b.Property("PasswordResetCount")
+ .HasColumnType("integer")
+ .HasColumnName("password_reset_count");
+
+ b.Property("Rank")
+ .HasColumnType("rank_type")
+ .HasColumnName("rank");
+
+ b.Property("ShockerControlLogCount")
+ .HasColumnType("integer")
+ .HasColumnName("shocker_control_log_count");
+
+ b.Property("ShockerCount")
+ .HasColumnType("integer")
+ .HasColumnName("shocker_count");
+
+ b.Property("ShockerShareCount")
+ .HasColumnType("integer")
+ .HasColumnName("shocker_share_count");
+
+ b.Property("ShockerShareLinkCount")
+ .HasColumnType("integer")
+ .HasColumnName("shocker_share_link_count");
+
+ b.Property("UserActivationCount")
+ .HasColumnType("integer")
+ .HasColumnName("user_activation_count");
+
+ b.ToTable((string)null);
+
+ b.ToView("admin_users_view", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ApiToken", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedByIp")
+ .IsRequired()
+ .HasMaxLength(40)
+ .HasColumnType("character varying(40)")
+ .HasColumnName("created_by_ip");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("LastUsed")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("last_used")
+ .HasDefaultValueSql("'-infinity'::timestamp without time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("name");
+
+ b.PrimitiveCollection("Permissions")
+ .IsRequired()
+ .HasColumnType("permission_type[]")
+ .HasColumnName("permissions");
+
+ b.Property("Token")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("token");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property("ValidUntil")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("valid_until");
+
+ b.HasKey("Id")
+ .HasName("api_tokens_pkey");
+
+ b.HasIndex("Token")
+ .IsUnique();
+
+ b.HasIndex("UserId")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.HasIndex("ValidUntil")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.ToTable("api_tokens", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.Device", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("name");
+
+ b.Property("Owner")
+ .HasColumnType("uuid")
+ .HasColumnName("owner");
+
+ b.Property("Token")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)")
+ .HasColumnName("token");
+
+ b.HasKey("Id")
+ .HasName("devices_pkey");
+
+ b.HasIndex("Owner")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.HasIndex("Token")
+ .IsUnique();
+
+ b.ToTable("devices", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.DeviceOtaUpdate", b =>
+ {
+ b.Property("Device")
+ .HasColumnType("uuid")
+ .HasColumnName("device");
+
+ b.Property("UpdateId")
+ .HasColumnType("integer")
+ .HasColumnName("update_id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("Message")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)")
+ .HasColumnName("message");
+
+ b.Property("Status")
+ .HasColumnType("ota_update_status")
+ .HasColumnName("status");
+
+ b.Property("Version")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("version");
+
+ b.HasKey("Device", "UpdateId")
+ .HasName("device_ota_updates_pkey");
+
+ b.HasIndex(new[] { "CreatedOn" }, "device_ota_updates_created_on_idx")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.ToTable("device_ota_updates", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.PasswordReset", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("Secret")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)")
+ .HasColumnName("secret");
+
+ b.Property("UsedOn")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("used_on");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("password_resets_pkey");
+
+ b.HasIndex("UserId")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.ToTable("password_resets", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShareRequest", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("Owner")
+ .HasColumnType("uuid")
+ .HasColumnName("owner");
+
+ b.Property("User")
+ .HasColumnType("uuid")
+ .HasColumnName("user");
+
+ b.HasKey("Id")
+ .HasName("shares_codes_pkey");
+
+ b.HasIndex("Owner")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.HasIndex("User");
+
+ b.ToTable("share_requests", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShareRequestsShocker", b =>
+ {
+ b.Property("ShareRequest")
+ .HasColumnType("uuid")
+ .HasColumnName("share_request");
+
+ b.Property("Shocker")
+ .HasColumnType("uuid")
+ .HasColumnName("shocker");
+
+ b.Property("LimitDuration")
+ .HasColumnType("integer")
+ .HasColumnName("limit_duration");
+
+ b.Property("LimitIntensity")
+ .HasColumnType("smallint")
+ .HasColumnName("limit_intensity");
+
+ b.Property("PermLive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_live");
+
+ b.Property("PermShock")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_shock");
+
+ b.Property("PermSound")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_sound");
+
+ b.Property("PermVibrate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_vibrate");
+
+ b.HasKey("ShareRequest", "Shocker")
+ .HasName("share_requests_shockers_pkey");
+
+ b.HasIndex("Shocker");
+
+ b.ToTable("share_requests_shockers", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.Shocker", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("Device")
+ .HasColumnType("uuid")
+ .HasColumnName("device");
+
+ b.Property("Model")
+ .HasColumnType("shocker_model_type")
+ .HasColumnName("model");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("name");
+
+ b.Property("Paused")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(false)
+ .HasColumnName("paused");
+
+ b.Property("RfId")
+ .HasColumnType("integer")
+ .HasColumnName("rf_id");
+
+ b.HasKey("Id")
+ .HasName("shockers_pkey");
+
+ b.HasIndex("Device")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.ToTable("shockers", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerControlLog", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("ControlledBy")
+ .HasColumnType("uuid")
+ .HasColumnName("controlled_by");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("CustomName")
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("custom_name");
+
+ b.Property("Duration")
+ .HasColumnType("bigint")
+ .HasColumnName("duration");
+
+ b.Property("Intensity")
+ .HasColumnType("smallint")
+ .HasColumnName("intensity");
+
+ b.Property("LiveControl")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(false)
+ .HasColumnName("live_control");
+
+ b.Property("ShockerId")
+ .HasColumnType("uuid")
+ .HasColumnName("shocker_id");
+
+ b.Property("Type")
+ .HasColumnType("control_type")
+ .HasColumnName("type");
+
+ b.HasKey("Id")
+ .HasName("shocker_control_logs_pkey");
+
+ b.HasIndex("ControlledBy");
+
+ b.HasIndex("ShockerId")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.ToTable("shocker_control_logs", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerShare", b =>
+ {
+ b.Property("ShockerId")
+ .HasColumnType("uuid")
+ .HasColumnName("shocker_id");
+
+ b.Property("SharedWith")
+ .HasColumnType("uuid")
+ .HasColumnName("shared_with");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("LimitDuration")
+ .HasColumnType("integer")
+ .HasColumnName("limit_duration");
+
+ b.Property("LimitIntensity")
+ .HasColumnType("smallint")
+ .HasColumnName("limit_intensity");
+
+ b.Property("Paused")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(false)
+ .HasColumnName("paused");
+
+ b.Property("PermLive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_live");
+
+ b.Property("PermShock")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_shock");
+
+ b.Property("PermSound")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_sound");
+
+ b.Property("PermVibrate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_vibrate");
+
+ b.HasKey("ShockerId", "SharedWith")
+ .HasName("shocker_shares_pkey");
+
+ b.HasIndex("SharedWith")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.ToTable("shocker_shares", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerShareCode", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("LimitDuration")
+ .HasColumnType("integer")
+ .HasColumnName("limit_duration");
+
+ b.Property("LimitIntensity")
+ .HasColumnType("smallint")
+ .HasColumnName("limit_intensity");
+
+ b.Property("PermShock")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_shock");
+
+ b.Property("PermSound")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_sound");
+
+ b.Property("PermVibrate")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_vibrate");
+
+ b.Property("ShockerId")
+ .HasColumnType("uuid")
+ .HasColumnName("shocker_id");
+
+ b.HasKey("Id")
+ .HasName("shocker_share_codes_pkey");
+
+ b.HasIndex("ShockerId");
+
+ b.ToTable("shocker_share_codes", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerSharesLink", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("ExpiresOn")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expires_on");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)")
+ .HasColumnName("name");
+
+ b.Property("OwnerId")
+ .HasColumnType("uuid")
+ .HasColumnName("owner_id");
+
+ b.HasKey("Id")
+ .HasName("shocker_shares_links_pkey");
+
+ b.HasIndex("OwnerId")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.ToTable("shocker_shares_links", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerSharesLinksShocker", b =>
+ {
+ b.Property("ShareLinkId")
+ .HasColumnType("uuid")
+ .HasColumnName("share_link_id");
+
+ b.Property("ShockerId")
+ .HasColumnType("uuid")
+ .HasColumnName("shocker_id");
+
+ b.Property("Cooldown")
+ .HasColumnType("integer")
+ .HasColumnName("cooldown");
+
+ b.Property("LimitDuration")
+ .HasColumnType("integer")
+ .HasColumnName("limit_duration");
+
+ b.Property("LimitIntensity")
+ .HasColumnType("smallint")
+ .HasColumnName("limit_intensity");
+
+ b.Property("Paused")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(false)
+ .HasColumnName("paused");
+
+ b.Property("PermLive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(true)
+ .HasColumnName("perm_live");
+
+ b.Property("PermShock")
+ .HasColumnType("boolean")
+ .HasColumnName("perm_shock");
+
+ b.Property("PermSound")
+ .HasColumnType("boolean")
+ .HasColumnName("perm_sound");
+
+ b.Property("PermVibrate")
+ .HasColumnType("boolean")
+ .HasColumnName("perm_vibrate");
+
+ b.HasKey("ShareLinkId", "ShockerId")
+ .HasName("shocker_shares_links_shockers_pkey");
+
+ b.HasIndex("ShockerId");
+
+ b.ToTable("shocker_shares_links_shockers", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.User", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasMaxLength(320)
+ .HasColumnType("character varying(320)")
+ .HasColumnName("email");
+
+ b.Property("EmailActived")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("boolean")
+ .HasDefaultValue(false)
+ .HasColumnName("email_actived");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)")
+ .HasColumnName("name")
+ .UseCollation("ndcoll");
+
+ b.Property("PasswordHash")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)")
+ .HasColumnName("password_hash");
+
+ b.Property("Rank")
+ .HasColumnType("rank_type")
+ .HasColumnName("rank");
+
+ b.HasKey("Id")
+ .HasName("users_pkey");
+
+ b.HasIndex("Email")
+ .IsUnique();
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ NpgsqlIndexBuilderExtensions.UseCollation(b.HasIndex("Name"), new[] { "ndcoll" });
+
+ b.ToTable("users", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.UsersActivation", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("Secret")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)")
+ .HasColumnName("secret");
+
+ b.Property("UsedOn")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("used_on");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("users_activation_pkey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("users_activation", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.UsersEmailChange", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasMaxLength(320)
+ .HasColumnType("character varying(320)")
+ .HasColumnName("email");
+
+ b.Property("Secret")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)")
+ .HasColumnName("secret");
+
+ b.Property("UsedOn")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("used_on");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("users_email_change_pkey");
+
+ b.HasIndex("CreatedOn")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.HasIndex("UsedOn")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("users_email_changes", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.UsersNameChange", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id"));
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property("CreatedOn")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_on")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("OldName")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)")
+ .HasColumnName("old_name");
+
+ b.HasKey("Id", "UserId")
+ .HasName("users_name_changes_pkey");
+
+ b.HasIndex("CreatedOn")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.HasIndex("OldName")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.HasIndex("UserId")
+ .HasAnnotation("Npgsql:StorageParameter:deduplicate_items", "true");
+
+ b.ToTable("users_name_changes", (string)null);
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ApiToken", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "User")
+ .WithMany("ApiTokens")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_user_id");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.Device", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "OwnerNavigation")
+ .WithMany("Devices")
+ .HasForeignKey("Owner")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("owner_user_id");
+
+ b.Navigation("OwnerNavigation");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.DeviceOtaUpdate", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.Device", "DeviceNavigation")
+ .WithMany("DeviceOtaUpdates")
+ .HasForeignKey("Device")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("device_ota_updates_device");
+
+ b.Navigation("DeviceNavigation");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.PasswordReset", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "User")
+ .WithMany("PasswordResets")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("user_id");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShareRequest", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "OwnerNavigation")
+ .WithMany("ShareRequestOwnerNavigations")
+ .HasForeignKey("Owner")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_share_requests_owner");
+
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "UserNavigation")
+ .WithMany("ShareRequestUserNavigations")
+ .HasForeignKey("User")
+ .OnDelete(DeleteBehavior.Cascade)
+ .HasConstraintName("fk_share_requests_user");
+
+ b.Navigation("OwnerNavigation");
+
+ b.Navigation("UserNavigation");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShareRequestsShocker", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.ShareRequest", "ShareRequestNavigation")
+ .WithMany("ShareRequestsShockers")
+ .HasForeignKey("ShareRequest")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_share_requests_shockers_share_request");
+
+ b.HasOne("OpenShock.Common.OpenShockDb.Shocker", "ShockerNavigation")
+ .WithMany("ShareRequestsShockers")
+ .HasForeignKey("Shocker")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_share_requests_shockers_shocker");
+
+ b.Navigation("ShareRequestNavigation");
+
+ b.Navigation("ShockerNavigation");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.Shocker", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.Device", "DeviceNavigation")
+ .WithMany("Shockers")
+ .HasForeignKey("Device")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("device_id");
+
+ b.Navigation("DeviceNavigation");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerControlLog", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "ControlledByNavigation")
+ .WithMany("ShockerControlLogs")
+ .HasForeignKey("ControlledBy")
+ .OnDelete(DeleteBehavior.Cascade)
+ .HasConstraintName("fk_controlled_by");
+
+ b.HasOne("OpenShock.Common.OpenShockDb.Shocker", "Shocker")
+ .WithMany("ShockerControlLogs")
+ .HasForeignKey("ShockerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_shocker_id");
+
+ b.Navigation("ControlledByNavigation");
+
+ b.Navigation("Shocker");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerShare", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "SharedWithNavigation")
+ .WithMany("ShockerShares")
+ .HasForeignKey("SharedWith")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("shared_with_user_id");
+
+ b.HasOne("OpenShock.Common.OpenShockDb.Shocker", "Shocker")
+ .WithMany("ShockerShares")
+ .HasForeignKey("ShockerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("ref_shocker_id");
+
+ b.Navigation("SharedWithNavigation");
+
+ b.Navigation("Shocker");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerShareCode", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.Shocker", "Shocker")
+ .WithMany("ShockerShareCodes")
+ .HasForeignKey("ShockerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_shocker_id");
+
+ b.Navigation("Shocker");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerSharesLink", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "Owner")
+ .WithMany("ShockerSharesLinks")
+ .HasForeignKey("OwnerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("owner_id");
+
+ b.Navigation("Owner");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerSharesLinksShocker", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.ShockerSharesLink", "ShareLink")
+ .WithMany("ShockerSharesLinksShockers")
+ .HasForeignKey("ShareLinkId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("share_link_id");
+
+ b.HasOne("OpenShock.Common.OpenShockDb.Shocker", "Shocker")
+ .WithMany("ShockerSharesLinksShockers")
+ .HasForeignKey("ShockerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("shocker_id");
+
+ b.Navigation("ShareLink");
+
+ b.Navigation("Shocker");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.UsersActivation", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "User")
+ .WithMany("UsersActivations")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("user_id");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.UsersEmailChange", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "User")
+ .WithMany("UsersEmailChanges")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_user_id");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.UsersNameChange", b =>
+ {
+ b.HasOne("OpenShock.Common.OpenShockDb.User", "User")
+ .WithMany("UsersNameChanges")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_user_id");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.Device", b =>
+ {
+ b.Navigation("DeviceOtaUpdates");
+
+ b.Navigation("Shockers");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShareRequest", b =>
+ {
+ b.Navigation("ShareRequestsShockers");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.Shocker", b =>
+ {
+ b.Navigation("ShareRequestsShockers");
+
+ b.Navigation("ShockerControlLogs");
+
+ b.Navigation("ShockerShareCodes");
+
+ b.Navigation("ShockerShares");
+
+ b.Navigation("ShockerSharesLinksShockers");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerSharesLink", b =>
+ {
+ b.Navigation("ShockerSharesLinksShockers");
+ });
+
+ modelBuilder.Entity("OpenShock.Common.OpenShockDb.User", b =>
+ {
+ b.Navigation("ApiTokens");
+
+ b.Navigation("Devices");
+
+ b.Navigation("PasswordResets");
+
+ b.Navigation("ShareRequestOwnerNavigations");
+
+ b.Navigation("ShareRequestUserNavigations");
+
+ b.Navigation("ShockerControlLogs");
+
+ b.Navigation("ShockerShares");
+
+ b.Navigation("ShockerSharesLinks");
+
+ b.Navigation("UsersActivations");
+
+ b.Navigation("UsersEmailChanges");
+
+ b.Navigation("UsersNameChanges");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Common/Migrations/20241122214013_Fix Petrainer998DR RFIDs.cs b/Common/Migrations/20241122214013_Fix Petrainer998DR RFIDs.cs
new file mode 100644
index 00000000..985b5e06
--- /dev/null
+++ b/Common/Migrations/20241122214013_Fix Petrainer998DR RFIDs.cs
@@ -0,0 +1,38 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace OpenShock.Common.Migrations
+{
+ ///
+ public partial class FixPetrainer998DRRFIDs : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql(
+ $"""
+ UPDATE shockers
+ SET
+ rf_id = ((rf_id)::bit(32) << 1)::integer
+ WHERE
+ model = 'petrainer998DR'
+ """
+ );
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql(
+ $"""
+ UPDATE shockers
+ SET
+ rf_id = ((rf_id)::bit(32) >> 1)::integer
+ WHERE
+ model = 'petrainer998DR'
+ """
+ );
+ }
+ }
+}
diff --git a/Common/Migrations/OpenShockContextModelSnapshot.cs b/Common/Migrations/OpenShockContextModelSnapshot.cs
index 4d84fc17..17e40578 100644
--- a/Common/Migrations/OpenShockContextModelSnapshot.cs
+++ b/Common/Migrations/OpenShockContextModelSnapshot.cs
@@ -18,7 +18,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False")
- .HasAnnotation("ProductVersion", "8.0.10")
+ .HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_type", new[] { "sound", "vibrate", "shock", "stop" });
@@ -48,7 +48,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasColumnType("character varying")
.HasColumnName("email");
- b.Property("EmailActived")
+ b.Property("EmailActivated")
.HasColumnType("boolean")
.HasColumnName("email_actived");
@@ -137,7 +137,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasColumnType("character varying(64)")
.HasColumnName("name");
- b.Property("Permissions")
+ b.PrimitiveCollection("Permissions")
.IsRequired()
.HasColumnType("permission_type[]")
.HasColumnName("permissions");
diff --git a/LiveControlGateway/Controllers/HubV1Controller.cs b/LiveControlGateway/Controllers/HubV1Controller.cs
index 13b5f80f..e157e72e 100644
--- a/LiveControlGateway/Controllers/HubV1Controller.cs
+++ b/LiveControlGateway/Controllers/HubV1Controller.cs
@@ -168,7 +168,7 @@ public override ValueTask Control(List> 1) : x.Id, // Fix for old hubs, their ids was serialized wrongly in the RFTransmitter, the V1 endpoint is being phased out, so this wont stay here forever
Intensity = x.Intensity,
Model = x.Model
}).ToList()