From 0e728802f4a1a4283a1979499c5329b342a4ff00 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Fri, 12 Jun 2026 16:26:49 +0200 Subject: [PATCH 1/5] refactor: centralise Postgres enum registration and fix petTrainer typo --- ...20260612142230_RefactorPgEnums.Designer.cs | 1518 +++++++++++++++++ .../20260612142230_RefactorPgEnums.cs | 66 + .../OpenShockContextModelSnapshot.cs | 6 +- Common/Models/ControlLimitMode.cs | 8 +- Common/Models/ControlType.cs | 16 +- Common/Models/OtaUpdateStatus.cs | 18 +- Common/Models/PasswordHashingAlgorithm.cs | 12 +- Common/Models/PermissionType.cs | 2 + Common/Models/RoleType.cs | 16 +- Common/Models/ShockerModelType.cs | 4 +- Common/OpenShockDb/ConfigurationItem.cs | 16 +- Common/OpenShockDb/NpgsqlEnumExtensions.cs | 69 + Common/OpenShockDb/OpenShockContext.cs | 23 +- Common/OpenShockDb/UserNameBlacklist.cs | 10 +- Common/Utils/PgEnumAttribute.cs | 16 + 15 files changed, 1741 insertions(+), 59 deletions(-) create mode 100644 Common/Migrations/20260612142230_RefactorPgEnums.Designer.cs create mode 100644 Common/Migrations/20260612142230_RefactorPgEnums.cs create mode 100644 Common/OpenShockDb/NpgsqlEnumExtensions.cs create mode 100644 Common/Utils/PgEnumAttribute.cs diff --git a/Common/Migrations/20260612142230_RefactorPgEnums.Designer.cs b/Common/Migrations/20260612142230_RefactorPgEnums.Designer.cs new file mode 100644 index 00000000..1524c1ef --- /dev/null +++ b/Common/Migrations/20260612142230_RefactorPgEnums.Designer.cs @@ -0,0 +1,1518 @@ +// +using System; +using System.Collections.Generic; +using System.Net; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; + +#nullable disable + +namespace OpenShock.Common.Migrations +{ + [DbContext(typeof(MigrationOpenShockContext))] + [Migration("20260612142230_RefactorPgEnums")] + partial class RefactorPgEnums + { + /// + 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", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "configuration_value_type", new[] { "string", "bool", "int", "float", "json" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_limit_mode", new[] { "clamp", "lerp" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_type", new[] { "stop", "shock", "vibrate", "sound" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "match_type_enum", new[] { "exact", "contains" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "ota_update_status", new[] { "started", "running", "finished", "error", "timeout" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "password_encryption_type", new[] { "bcrypt_enhanced", "pbkdf2" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "permission_type", new[] { "shockers.use", "shockers.edit", "shockers.pause", "devices.edit", "devices.auth" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "role_type", new[] { "support", "staff", "admin", "system" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "shocker_model_type", new[] { "caiXianlin", "petTrainer", "petrainer998DR", "wellturnT330" }); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.AdminUsersView", b => + { + b.Property("ActivatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("activated_at"); + + b.Property("ApiTokenCount") + .HasColumnType("integer") + .HasColumnName("api_token_count"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("DeactivatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deactivated_at"); + + b.Property("DeactivatedByUserId") + .HasColumnType("uuid") + .HasColumnName("deactivated_by_user_id"); + + b.Property("DeviceCount") + .HasColumnType("integer") + .HasColumnName("device_count"); + + b.Property("Email") + .IsRequired() + .HasColumnType("character varying") + .HasColumnName("email"); + + 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") + .HasColumnType("character varying") + .HasColumnName("password_hash_type"); + + b.Property("PasswordResetCount") + .HasColumnType("integer") + .HasColumnName("password_reset_count"); + + b.Property("Roles") + .IsRequired() + .HasColumnType("role_type[]") + .HasColumnName("roles"); + + b.Property("ShockerControlLogCount") + .HasColumnType("integer") + .HasColumnName("shocker_control_log_count"); + + b.Property("ShockerCount") + .HasColumnType("integer") + .HasColumnName("shocker_count"); + + b.Property("ShockerPublicShareCount") + .HasColumnType("integer") + .HasColumnName("shocker_public_share_count"); + + b.Property("ShockerUserShareCount") + .HasColumnType("integer") + .HasColumnName("shocker_user_share_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("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CreatedByIp") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("created_by_ip"); + + b.Property("LastUsed") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_used"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("name"); + + b.PrimitiveCollection>("Permissions") + .IsRequired() + .HasColumnType("permission_type[]") + .HasColumnName("permissions"); + + b.Property("ShockerControlDurationMax") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(65535) + .HasColumnName("shocker_control_duration_max"); + + b.Property("ShockerControlDurationMin") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(300) + .HasColumnName("shocker_control_duration_min"); + + b.Property("ShockerControlDurationMode") + .ValueGeneratedOnAdd() + .HasColumnType("control_limit_mode") + .HasDefaultValue(ControlLimitMode.Clamp) + .HasColumnName("shocker_control_duration_mode"); + + b.Property("ShockerControlIntensityMax") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)100) + .HasColumnName("shocker_control_intensity_max"); + + b.Property("ShockerControlIntensityMin") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)0) + .HasColumnName("shocker_control_intensity_min"); + + b.Property("ShockerControlIntensityMode") + .ValueGeneratedOnAdd() + .HasColumnType("control_limit_mode") + .HasDefaultValue(ControlLimitMode.Clamp) + .HasColumnName("shocker_control_intensity_mode"); + + b.Property("ShockerControlPaused") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("shocker_control_paused"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("token_hash") + .UseCollation("C"); + + 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("TokenHash") + .IsUnique(); + + b.HasIndex("UserId"); + + b.HasIndex("ValidUntil"); + + b.ToTable("api_tokens", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.ApiTokenReport", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AffectedCount") + .HasColumnType("integer") + .HasColumnName("affected_count"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("IpAddress") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("ip_address"); + + b.Property("IpCountry") + .HasColumnType("text") + .HasColumnName("ip_country"); + + b.Property("SubmittedCount") + .HasColumnType("integer") + .HasColumnName("submitted_count"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("api_token_reports_pkey"); + + b.HasIndex("UserId"); + + b.ToTable("api_token_reports", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.ConfigurationItem", b => + { + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name") + .UseCollation("C"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Type") + .HasColumnType("configuration_value_type") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text") + .HasColumnName("value"); + + b.HasKey("Name") + .HasName("configuration_pkey"); + + b.ToTable("configuration", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.Device", 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("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("name"); + + b.Property("OwnerId") + .HasColumnType("uuid") + .HasColumnName("owner_id"); + + b.Property("Token") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("token") + .UseCollation("C"); + + b.HasKey("Id") + .HasName("devices_pkey"); + + b.HasIndex("OwnerId"); + + b.HasIndex("Token") + .IsUnique(); + + b.ToTable("devices", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.DeviceOtaUpdate", b => + { + b.Property("DeviceId") + .HasColumnType("uuid") + .HasColumnName("device_id"); + + b.Property("UpdateId") + .HasColumnType("integer") + .HasColumnName("update_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .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("DeviceId", "UpdateId") + .HasName("device_ota_updates_pkey"); + + b.HasIndex(new[] { "CreatedAt" }, "device_ota_updates_created_at_idx"); + + b.ToTable("device_ota_updates", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.DiscordWebhook", b => + { + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("WebhookId") + .HasColumnType("bigint") + .HasColumnName("webhook_id"); + + b.Property("WebhookToken") + .IsRequired() + .HasColumnType("text") + .HasColumnName("webhook_token"); + + b.HasKey("Name") + .HasName("discord_webhooks_pkey"); + + b.ToTable("discord_webhooks", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.EmailProviderBlacklist", 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("Domain") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("domain") + .UseCollation("ndcoll"); + + b.HasKey("Id") + .HasName("email_provider_blacklist_pkey"); + + b.HasIndex("Domain") + .IsUnique(); + + NpgsqlIndexBuilderExtensions.UseCollation(b.HasIndex("Domain"), new[] { "ndcoll" }); + + b.ToTable("email_provider_blacklist", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.PublicShare", 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("ExpiresAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("expires_at"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("name"); + + b.Property("OwnerId") + .HasColumnType("uuid") + .HasColumnName("owner_id"); + + b.HasKey("Id") + .HasName("public_shares_pkey"); + + b.HasIndex("OwnerId"); + + b.ToTable("public_shares", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.PublicShareShocker", b => + { + b.Property("PublicShareId") + .HasColumnType("uuid") + .HasColumnName("public_share_id"); + + b.Property("ShockerId") + .HasColumnType("uuid") + .HasColumnName("shocker_id"); + + b.Property("AllowLiveControl") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("allow_livecontrol"); + + b.Property("AllowShock") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_shock"); + + b.Property("AllowSound") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_sound"); + + b.Property("AllowVibrate") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_vibrate"); + + b.Property("Cooldown") + .HasColumnType("integer") + .HasColumnName("cooldown"); + + b.Property("IsPaused") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_paused"); + + b.Property("MaxDuration") + .HasColumnType("integer") + .HasColumnName("max_duration"); + + b.Property("MaxIntensity") + .HasColumnType("smallint") + .HasColumnName("max_intensity"); + + b.HasKey("PublicShareId", "ShockerId") + .HasName("public_share_shockers_pkey"); + + b.HasIndex("ShockerId"); + + b.ToTable("public_share_shockers", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.Shocker", 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("DeviceId") + .HasColumnType("uuid") + .HasColumnName("device_id"); + + b.Property("IsPaused") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_paused"); + + b.Property("Model") + .HasColumnType("shocker_model_type") + .HasColumnName("model"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("name"); + + b.Property("RfId") + .HasColumnType("integer") + .HasColumnName("rf_id"); + + b.HasKey("Id") + .HasName("shockers_pkey"); + + b.HasIndex("DeviceId"); + + b.ToTable("shockers", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerControlLog", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ControlledByUserId") + .HasColumnType("uuid") + .HasColumnName("controlled_by_user_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .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("ControlledByUserId"); + + b.HasIndex("ShockerId"); + + b.ToTable("shocker_control_logs", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerShareCode", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AllowLiveControl") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_livecontrol"); + + b.Property("AllowShock") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_shock"); + + b.Property("AllowSound") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_sound"); + + b.Property("AllowVibrate") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_vibrate"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("IsPaused") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_paused"); + + b.Property("MaxDuration") + .HasColumnType("integer") + .HasColumnName("max_duration"); + + b.Property("MaxIntensity") + .HasColumnType("smallint") + .HasColumnName("max_intensity"); + + 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.User", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ActivatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("activated_at"); + + 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("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("name") + .UseCollation("ndcoll"); + + b.Property("PasswordHash") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("password_hash") + .UseCollation("C"); + + b.PrimitiveCollection>("Roles") + .IsRequired() + .HasColumnType("role_type[]") + .HasColumnName("roles"); + + b.Property("SecurityStamp") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("security_stamp") + .HasDefaultValueSql("gen_random_uuid()"); + + 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.UserActivationRequest", b => + { + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("EmailSendAttempts") + .HasColumnType("integer") + .HasColumnName("email_send_attempts"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("token_hash") + .UseCollation("C"); + + b.HasKey("UserId") + .HasName("user_activation_requests_pkey"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.ToTable("user_activation_requests", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserDeactivation", b => + { + b.Property("DeactivatedUserId") + .HasColumnType("uuid") + .HasColumnName("deactivated_user_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("DeactivatedByUserId") + .HasColumnType("uuid") + .HasColumnName("deactivated_by_user_id"); + + b.Property("DeleteLater") + .HasColumnType("boolean") + .HasColumnName("delete_later"); + + b.Property("UserModerationId") + .HasColumnType("uuid") + .HasColumnName("user_moderation_id"); + + b.HasKey("DeactivatedUserId") + .HasName("user_deactivations_pkey"); + + b.HasIndex("DeactivatedByUserId"); + + b.ToTable("user_deactivations", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserEmailChange", 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("NewEmail") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)") + .HasColumnName("email_new"); + + b.Property("OldEmail") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)") + .HasColumnName("email_old"); + + b.Property("SecurityStampAtCreate") + .HasColumnType("uuid") + .HasColumnName("security_stamp_at_create"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("token_hash") + .UseCollation("C"); + + b.Property("UsedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("used_at"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("user_email_changes_pkey"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UsedAt"); + + b.HasIndex("UserId"); + + b.ToTable("user_email_changes", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserNameBlacklist", 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("MatchType") + .HasColumnType("match_type_enum") + .HasColumnName("match_type"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("value") + .UseCollation("ndcoll"); + + b.HasKey("Id") + .HasName("user_name_blacklist_pkey"); + + b.HasIndex("Value") + .IsUnique(); + + NpgsqlIndexBuilderExtensions.UseCollation(b.HasIndex("Value"), new[] { "ndcoll" }); + + b.ToTable("user_name_blacklist", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserNameChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("OldName") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("old_name"); + + b.HasKey("Id", "UserId") + .HasName("user_name_changes_pkey"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("OldName"); + + b.HasIndex("UserId"); + + b.ToTable("user_name_changes", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserOAuthConnection", b => + { + b.Property("ProviderKey") + .HasColumnType("text") + .HasColumnName("provider_key") + .UseCollation("C"); + + b.Property("ExternalId") + .HasColumnType("text") + .HasColumnName("external_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("DisplayName") + .HasColumnType("text") + .HasColumnName("display_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("ProviderKey", "ExternalId") + .HasName("user_oauth_connections_pkey"); + + b.HasIndex("UserId"); + + b.ToTable("user_oauth_connections", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserPasswordReset", 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("SecurityStampAtCreate") + .HasColumnType("uuid") + .HasColumnName("security_stamp_at_create"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("token_hash") + .UseCollation("C"); + + b.Property("UsedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("used_at"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("user_password_resets_pkey"); + + b.HasIndex("UserId"); + + b.ToTable("user_password_resets", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserShare", b => + { + b.Property("SharedWithUserId") + .HasColumnType("uuid") + .HasColumnName("shared_with_user_id"); + + b.Property("ShockerId") + .HasColumnType("uuid") + .HasColumnName("shocker_id"); + + b.Property("AllowLiveControl") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_livecontrol"); + + b.Property("AllowShock") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_shock"); + + b.Property("AllowSound") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_sound"); + + b.Property("AllowVibrate") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_vibrate"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("IsPaused") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_paused"); + + b.Property("MaxDuration") + .HasColumnType("integer") + .HasColumnName("max_duration"); + + b.Property("MaxIntensity") + .HasColumnType("smallint") + .HasColumnName("max_intensity"); + + b.HasKey("SharedWithUserId", "ShockerId") + .HasName("user_shares_pkey"); + + b.HasIndex("SharedWithUserId"); + + b.HasIndex("ShockerId"); + + b.ToTable("user_shares", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserShareInvite", 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("OwnerId") + .HasColumnType("uuid") + .HasColumnName("owner_id"); + + b.Property("RecipientUserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("user_share_invites_pkey"); + + b.HasIndex("OwnerId"); + + b.HasIndex("RecipientUserId"); + + b.ToTable("user_share_invites", (string)null); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserShareInviteShocker", b => + { + b.Property("InviteId") + .HasColumnType("uuid") + .HasColumnName("invite_id"); + + b.Property("ShockerId") + .HasColumnType("uuid") + .HasColumnName("shocker_id"); + + b.Property("AllowLiveControl") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_livecontrol"); + + b.Property("AllowShock") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_shock"); + + b.Property("AllowSound") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_sound"); + + b.Property("AllowVibrate") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("allow_vibrate"); + + b.Property("IsPaused") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("is_paused"); + + b.Property("MaxDuration") + .HasColumnType("integer") + .HasColumnName("max_duration"); + + b.Property("MaxIntensity") + .HasColumnType("smallint") + .HasColumnName("max_intensity"); + + b.HasKey("InviteId", "ShockerId") + .HasName("user_share_invite_shockers_pkey"); + + b.HasIndex("ShockerId"); + + b.ToTable("user_share_invite_shockers", (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_api_tokens_user_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.ApiTokenReport", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "ReportedByUser") + .WithMany("ReportedApiTokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_api_token_reports_reported_by_user_id"); + + b.Navigation("ReportedByUser"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.Device", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "Owner") + .WithMany("Devices") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_devices_owner_id"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.DeviceOtaUpdate", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.Device", "Device") + .WithMany("OtaUpdates") + .HasForeignKey("DeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_device_ota_updates_device_id"); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.PublicShare", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "Owner") + .WithMany("OwnedPublicShares") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_public_shares_owner_id"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.PublicShareShocker", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.PublicShare", "PublicShare") + .WithMany("ShockerMappings") + .HasForeignKey("PublicShareId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_public_share_shockers_public_share_id"); + + b.HasOne("OpenShock.Common.OpenShockDb.Shocker", "Shocker") + .WithMany("PublicShareMappings") + .HasForeignKey("ShockerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_public_share_shockers_shocker_id"); + + b.Navigation("PublicShare"); + + b.Navigation("Shocker"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.Shocker", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.Device", "Device") + .WithMany("Shockers") + .HasForeignKey("DeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_shockers_device_id"); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.ShockerControlLog", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "ControlledByUser") + .WithMany("ShockerControlLogs") + .HasForeignKey("ControlledByUserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("fk_shocker_control_logs_controlled_by_user_id"); + + b.HasOne("OpenShock.Common.OpenShockDb.Shocker", "Shocker") + .WithMany("ShockerControlLogs") + .HasForeignKey("ShockerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_shocker_control_logs_shocker_id"); + + b.Navigation("ControlledByUser"); + + 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_share_codes_shocker_id"); + + b.Navigation("Shocker"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserActivationRequest", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "User") + .WithOne("UserActivationRequest") + .HasForeignKey("OpenShock.Common.OpenShockDb.UserActivationRequest", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_activation_requests_user_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserDeactivation", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "DeactivatedByUser") + .WithMany() + .HasForeignKey("DeactivatedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_deactivations_deactivated_by_user_id"); + + b.HasOne("OpenShock.Common.OpenShockDb.User", "DeactivatedUser") + .WithOne("UserDeactivation") + .HasForeignKey("OpenShock.Common.OpenShockDb.UserDeactivation", "DeactivatedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_deactivations_deactivated_user_id"); + + b.Navigation("DeactivatedByUser"); + + b.Navigation("DeactivatedUser"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserEmailChange", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "User") + .WithMany("EmailChanges") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_email_changes_user_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserNameChange", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "User") + .WithMany("NameChanges") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_name_changes_user_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserOAuthConnection", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "User") + .WithMany("OAuthConnections") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_oauth_connections_user_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserPasswordReset", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "User") + .WithMany("PasswordResets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_password_resets_user_id"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserShare", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "SharedWithUser") + .WithMany("IncomingUserShares") + .HasForeignKey("SharedWithUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_shares_shared_with_user_id"); + + b.HasOne("OpenShock.Common.OpenShockDb.Shocker", "Shocker") + .WithMany("UserShares") + .HasForeignKey("ShockerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_shares_shocker_id"); + + b.Navigation("SharedWithUser"); + + b.Navigation("Shocker"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserShareInvite", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.User", "Owner") + .WithMany("OutgoingUserShareInvites") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_share_invites_owner_id"); + + b.HasOne("OpenShock.Common.OpenShockDb.User", "RecipientUser") + .WithMany("IncomingUserShareInvites") + .HasForeignKey("RecipientUserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("fk_user_share_invites_recipient_user_id"); + + b.Navigation("Owner"); + + b.Navigation("RecipientUser"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserShareInviteShocker", b => + { + b.HasOne("OpenShock.Common.OpenShockDb.UserShareInvite", "Invite") + .WithMany("ShockerMappings") + .HasForeignKey("InviteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_share_invite_shockers_invite_id"); + + b.HasOne("OpenShock.Common.OpenShockDb.Shocker", "Shocker") + .WithMany("UserShareInviteShockerMappings") + .HasForeignKey("ShockerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_share_invite_shockers_shocker_id"); + + b.Navigation("Invite"); + + b.Navigation("Shocker"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.Device", b => + { + b.Navigation("OtaUpdates"); + + b.Navigation("Shockers"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.PublicShare", b => + { + b.Navigation("ShockerMappings"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.Shocker", b => + { + b.Navigation("PublicShareMappings"); + + b.Navigation("ShockerControlLogs"); + + b.Navigation("ShockerShareCodes"); + + b.Navigation("UserShareInviteShockerMappings"); + + b.Navigation("UserShares"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.User", b => + { + b.Navigation("ApiTokens"); + + b.Navigation("Devices"); + + b.Navigation("EmailChanges"); + + b.Navigation("IncomingUserShareInvites"); + + b.Navigation("IncomingUserShares"); + + b.Navigation("NameChanges"); + + b.Navigation("OAuthConnections"); + + b.Navigation("OutgoingUserShareInvites"); + + b.Navigation("OwnedPublicShares"); + + b.Navigation("PasswordResets"); + + b.Navigation("ReportedApiTokens"); + + b.Navigation("ShockerControlLogs"); + + b.Navigation("UserActivationRequest"); + + b.Navigation("UserDeactivation"); + }); + + modelBuilder.Entity("OpenShock.Common.OpenShockDb.UserShareInvite", b => + { + b.Navigation("ShockerMappings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Common/Migrations/20260612142230_RefactorPgEnums.cs b/Common/Migrations/20260612142230_RefactorPgEnums.cs new file mode 100644 index 00000000..7b83c2ae --- /dev/null +++ b/Common/Migrations/20260612142230_RefactorPgEnums.cs @@ -0,0 +1,66 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OpenShock.Common.Migrations +{ + /// + public partial class RefactorPgEnums : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("ALTER TYPE shocker_model_type RENAME VALUE 'petTrainer' TO 'petrainer';"); + + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False") + .Annotation("Npgsql:Enum:configuration_value_type", "string,bool,int,float,json") + .Annotation("Npgsql:Enum:control_limit_mode", "clamp,lerp") + .Annotation("Npgsql:Enum:control_type", "stop,shock,vibrate,sound") + .Annotation("Npgsql:Enum:match_type_enum", "exact,contains") + .Annotation("Npgsql:Enum:ota_update_status", "started,running,finished,error,timeout") + .Annotation("Npgsql:Enum:password_encryption_type", "bcrypt_enhanced,pbkdf2") + .Annotation("Npgsql:Enum:permission_type", "shockers.use,shockers.edit,shockers.pause,devices.edit,devices.auth") + .Annotation("Npgsql:Enum:role_type", "support,staff,admin,system") + .Annotation("Npgsql:Enum:shocker_model_type", "caiXianlin,petrainer,petrainer998DR,wellturnT330") + .OldAnnotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False") + .OldAnnotation("Npgsql:Enum:configuration_value_type", "string,bool,int,float,json") + .OldAnnotation("Npgsql:Enum:control_limit_mode", "clamp,lerp") + .OldAnnotation("Npgsql:Enum:control_type", "sound,vibrate,shock,stop") + .OldAnnotation("Npgsql:Enum:match_type_enum", "exact,contains") + .OldAnnotation("Npgsql:Enum:ota_update_status", "started,running,finished,error,timeout") + .OldAnnotation("Npgsql:Enum:password_encryption_type", "pbkdf2,bcrypt_enhanced") + .OldAnnotation("Npgsql:Enum:permission_type", "shockers.use,shockers.edit,shockers.pause,devices.edit,devices.auth") + .OldAnnotation("Npgsql:Enum:role_type", "support,staff,admin,system") + .OldAnnotation("Npgsql:Enum:shocker_model_type", "caiXianlin,petTrainer,petrainer998DR,wellturnT330"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False") + .Annotation("Npgsql:Enum:configuration_value_type", "string,bool,int,float,json") + .Annotation("Npgsql:Enum:control_limit_mode", "clamp,lerp") + .Annotation("Npgsql:Enum:control_type", "sound,vibrate,shock,stop") + .Annotation("Npgsql:Enum:match_type_enum", "exact,contains") + .Annotation("Npgsql:Enum:ota_update_status", "started,running,finished,error,timeout") + .Annotation("Npgsql:Enum:password_encryption_type", "pbkdf2,bcrypt_enhanced") + .Annotation("Npgsql:Enum:permission_type", "shockers.use,shockers.edit,shockers.pause,devices.edit,devices.auth") + .Annotation("Npgsql:Enum:role_type", "support,staff,admin,system") + .Annotation("Npgsql:Enum:shocker_model_type", "caiXianlin,petTrainer,petrainer998DR,wellturnT330") + .OldAnnotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False") + .OldAnnotation("Npgsql:Enum:configuration_value_type", "string,bool,int,float,json") + .OldAnnotation("Npgsql:Enum:control_limit_mode", "clamp,lerp") + .OldAnnotation("Npgsql:Enum:control_type", "stop,shock,vibrate,sound") + .OldAnnotation("Npgsql:Enum:match_type_enum", "exact,contains") + .OldAnnotation("Npgsql:Enum:ota_update_status", "started,running,finished,error,timeout") + .OldAnnotation("Npgsql:Enum:password_encryption_type", "bcrypt_enhanced,pbkdf2") + .OldAnnotation("Npgsql:Enum:permission_type", "shockers.use,shockers.edit,shockers.pause,devices.edit,devices.auth") + .OldAnnotation("Npgsql:Enum:role_type", "support,staff,admin,system") + .OldAnnotation("Npgsql:Enum:shocker_model_type", "caiXianlin,petrainer,petrainer998DR,wellturnT330"); + + migrationBuilder.Sql("ALTER TYPE shocker_model_type RENAME VALUE 'petrainer' TO 'petTrainer';"); + } + } +} diff --git a/Common/Migrations/OpenShockContextModelSnapshot.cs b/Common/Migrations/OpenShockContextModelSnapshot.cs index 81f0c618..f736b4e3 100644 --- a/Common/Migrations/OpenShockContextModelSnapshot.cs +++ b/Common/Migrations/OpenShockContextModelSnapshot.cs @@ -26,13 +26,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "configuration_value_type", new[] { "string", "bool", "int", "float", "json" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_limit_mode", new[] { "clamp", "lerp" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_type", new[] { "sound", "vibrate", "shock", "stop" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_type", new[] { "stop", "shock", "vibrate", "sound" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "match_type_enum", new[] { "exact", "contains" }); 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, "password_encryption_type", new[] { "bcrypt_enhanced", "pbkdf2" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "permission_type", new[] { "shockers.use", "shockers.edit", "shockers.pause", "devices.edit", "devices.auth" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "role_type", new[] { "support", "staff", "admin", "system" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "shocker_model_type", new[] { "caiXianlin", "petTrainer", "petrainer998DR", "wellturnT330" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "shocker_model_type", new[] { "caiXianlin", "petrainer", "petrainer998DR", "wellturnT330" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => diff --git a/Common/Models/ControlLimitMode.cs b/Common/Models/ControlLimitMode.cs index b2de7f81..bf4b48ba 100644 --- a/Common/Models/ControlLimitMode.cs +++ b/Common/Models/ControlLimitMode.cs @@ -1,17 +1,21 @@ +using NpgsqlTypes; +using OpenShock.Common.Utils; + namespace OpenShock.Common.Models; /// /// Determines how a per-token min/max limit is applied to an incoming control value. /// +[PgEnum] public enum ControlLimitMode { /// /// Clamp the incoming value into the [min, max] range. /// - Clamp = 0, + [PgName("clamp")] Clamp = 0, /// /// Linearly remap the full input range onto [min, max]. /// - Lerp = 1 + [PgName("lerp")] Lerp = 1, } diff --git a/Common/Models/ControlType.cs b/Common/Models/ControlType.cs index c10b7221..02a29243 100644 --- a/Common/Models/ControlType.cs +++ b/Common/Models/ControlType.cs @@ -1,9 +1,13 @@ -namespace OpenShock.Common.Models; +using NpgsqlTypes; +using OpenShock.Common.Utils; +namespace OpenShock.Common.Models; + +[PgEnum] public enum ControlType { - Stop = 0, - Shock = 1, - Vibrate = 2, - Sound = 3 -} \ No newline at end of file + [PgName("stop")] Stop = 0, + [PgName("shock")] Shock = 1, + [PgName("vibrate")] Vibrate = 2, + [PgName("sound")] Sound = 3, +} diff --git a/Common/Models/OtaUpdateStatus.cs b/Common/Models/OtaUpdateStatus.cs index 27c91151..9ed2e899 100644 --- a/Common/Models/OtaUpdateStatus.cs +++ b/Common/Models/OtaUpdateStatus.cs @@ -1,10 +1,14 @@ -namespace OpenShock.Common.Models; +using NpgsqlTypes; +using OpenShock.Common.Utils; +namespace OpenShock.Common.Models; + +[PgEnum] public enum OtaUpdateStatus { - Started, - Running, - Finished, - Error, - Timeout -} \ No newline at end of file + [PgName("started")] Started, + [PgName("running")] Running, + [PgName("finished")] Finished, + [PgName("error")] Error, + [PgName("timeout")] Timeout, +} diff --git a/Common/Models/PasswordHashingAlgorithm.cs b/Common/Models/PasswordHashingAlgorithm.cs index 4b19f361..9c3be23e 100644 --- a/Common/Models/PasswordHashingAlgorithm.cs +++ b/Common/Models/PasswordHashingAlgorithm.cs @@ -1,9 +1,13 @@ -// ReSharper disable InconsistentNaming +// ReSharper disable InconsistentNaming +using NpgsqlTypes; +using OpenShock.Common.Utils; + namespace OpenShock.Common.Models; +[PgEnum("password_encryption_type")] public enum PasswordHashingAlgorithm { Unknown = -1, - BCrypt = 0, - PBKDF2 = 1, -}; \ No newline at end of file + [PgName("bcrypt_enhanced")] BCrypt = 0, + [PgName("pbkdf2")] PBKDF2 = 1, +}; diff --git a/Common/Models/PermissionType.cs b/Common/Models/PermissionType.cs index f1fa6e03..0219b10e 100644 --- a/Common/Models/PermissionType.cs +++ b/Common/Models/PermissionType.cs @@ -2,11 +2,13 @@ using System.Text.Json.Serialization; using NpgsqlTypes; using OpenShock.Common.JsonSerialization; +using OpenShock.Common.Utils; // ReSharper disable InconsistentNaming namespace OpenShock.Common.Models; +[PgEnum] [JsonConverter(typeof(PermissionTypeConverter))] public enum PermissionType { diff --git a/Common/Models/RoleType.cs b/Common/Models/RoleType.cs index 1cb64b97..03ccc153 100644 --- a/Common/Models/RoleType.cs +++ b/Common/Models/RoleType.cs @@ -1,9 +1,13 @@ -namespace OpenShock.Common.Models; +using NpgsqlTypes; +using OpenShock.Common.Utils; +namespace OpenShock.Common.Models; + +[PgEnum] public enum RoleType { - Support, - Staff, - Admin, - System -} \ No newline at end of file + [PgName("support")] Support, + [PgName("staff")] Staff, + [PgName("admin")] Admin, + [PgName("system")] System, +} diff --git a/Common/Models/ShockerModelType.cs b/Common/Models/ShockerModelType.cs index 84690157..b1eb8cb3 100644 --- a/Common/Models/ShockerModelType.cs +++ b/Common/Models/ShockerModelType.cs @@ -1,11 +1,13 @@ using NpgsqlTypes; +using OpenShock.Common.Utils; namespace OpenShock.Common.Models; +[PgEnum] public enum ShockerModelType { [PgName("caiXianlin")] CaiXianlin = 0, - [PgName("petTrainer")] PetTrainer = 1, // Misspelled, should be "petrainer", + [PgName("petrainer")] PetTrainer = 1, [PgName("petrainer998DR")] Petrainer998DR = 2, [PgName("wellturnT330")] WellturnT330 = 3, } \ No newline at end of file diff --git a/Common/OpenShockDb/ConfigurationItem.cs b/Common/OpenShockDb/ConfigurationItem.cs index eb7c9959..d0e58e0c 100644 --- a/Common/OpenShockDb/ConfigurationItem.cs +++ b/Common/OpenShockDb/ConfigurationItem.cs @@ -1,12 +1,16 @@ -namespace OpenShock.Common.OpenShockDb; +using NpgsqlTypes; +using OpenShock.Common.Utils; +namespace OpenShock.Common.OpenShockDb; + +[PgEnum] public enum ConfigurationValueType { - String, - Bool, - Int, - Float, - Json + [PgName("string")] String, + [PgName("bool")] Bool, + [PgName("int")] Int, + [PgName("float")] Float, + [PgName("json")] Json, } public sealed class ConfigurationItem diff --git a/Common/OpenShockDb/NpgsqlEnumExtensions.cs b/Common/OpenShockDb/NpgsqlEnumExtensions.cs new file mode 100644 index 00000000..7adae5b8 --- /dev/null +++ b/Common/OpenShockDb/NpgsqlEnumExtensions.cs @@ -0,0 +1,69 @@ +using System.Reflection; +using System.Text.RegularExpressions; +using Microsoft.EntityFrameworkCore; +using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +using NpgsqlTypes; +using OpenShock.Common.Models; +using OpenShock.Common.Utils; + +namespace OpenShock.Common.OpenShockDb; + +public static partial class NpgsqlEnumExtensions +{ + [GeneratedRegex("([a-z])([A-Z0-9])")] + private static partial Regex SnakeCaseRegex(); + + private static string ToSnakeCase(string name) => + SnakeCaseRegex().Replace(name, "$1_$2").ToLowerInvariant(); + + private readonly record struct PgEnumInfo( + Action Map, + Action Register); + + private static PgEnumInfo BuildInfo() where TEnum : struct, Enum + { + var type = typeof(TEnum); + var attr = type.GetCustomAttribute() + ?? throw new InvalidOperationException($"{type.Name} is missing [PgEnum]"); + + var pgTypeName = attr.Name ?? ToSnakeCase(type.Name); + + var members = type + .GetFields(BindingFlags.Public | BindingFlags.Static) + .Select(f => f.GetCustomAttribute()?.PgName) + .Where(n => n is not null) + .Cast() + .ToArray(); + + return new PgEnumInfo( + Map: b => b.MapEnum(pgTypeName), + Register: m => m.HasPostgresEnum(attr.Schema, pgTypeName, members)); + } + + private static readonly PgEnumInfo[] Enums = + [ + BuildInfo(), + BuildInfo(), + BuildInfo(), + BuildInfo(), + BuildInfo(), + BuildInfo(), + BuildInfo(), + BuildInfo(), + BuildInfo(), + ]; + + public static NpgsqlDbContextOptionsBuilder MapPgEnums(this NpgsqlDbContextOptionsBuilder builder) + { + foreach (var info in Enums) + info.Map(builder); + return builder; + } + + public static ModelBuilder RegisterPgEnums(this ModelBuilder modelBuilder) + { + foreach (var info in Enums) + info.Register(modelBuilder); + return modelBuilder; + } +} diff --git a/Common/OpenShockDb/OpenShockContext.cs b/Common/OpenShockDb/OpenShockContext.cs index 66de70f0..f598236d 100644 --- a/Common/OpenShockDb/OpenShockContext.cs +++ b/Common/OpenShockDb/OpenShockContext.cs @@ -61,17 +61,7 @@ public OpenShockContext(DbContextOptions options) public static void ConfigureOptionsBuilder(DbContextOptionsBuilder optionsBuilder, string connectionString, bool debug) { - optionsBuilder.UseNpgsql(connectionString, npgsqlBuilder => - { - npgsqlBuilder.MapEnum(); - npgsqlBuilder.MapEnum(); - npgsqlBuilder.MapEnum(); - npgsqlBuilder.MapEnum(); - npgsqlBuilder.MapEnum(); - npgsqlBuilder.MapEnum(); - npgsqlBuilder.MapEnum(); - npgsqlBuilder.MapEnum(); - }); + optionsBuilder.UseNpgsql(connectionString, npgsqlBuilder => npgsqlBuilder.MapPgEnums()); if (debug) { @@ -141,16 +131,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder - .HasPostgresEnum("control_type", ["sound", "vibrate", "shock", "stop"]) - .HasPostgresEnum("control_limit_mode", ["clamp", "lerp"]) - .HasPostgresEnum("ota_update_status", ["started", "running", "finished", "error", "timeout"]) - .HasPostgresEnum("password_encryption_type", ["pbkdf2", "bcrypt_enhanced"]) - .HasPostgresEnum("permission_type", - ["shockers.use", "shockers.edit", "shockers.pause", "devices.edit", "devices.auth"]) - .HasPostgresEnum("role_type", ["support", "staff", "admin", "system"]) - .HasPostgresEnum("shocker_model_type", ["caiXianlin", "petTrainer", "petrainer998DR", "wellturnT330"]) - .HasPostgresEnum("match_type_enum", ["exact", "contains"]) - .HasPostgresEnum("configuration_value_type", ["string", "bool", "int", "float", "json"]) + .RegisterPgEnums() .HasCollation("public", "ndcoll", "und-u-ks-level2", "icu", false); // Add case-insensitive, accent-sensitive comparison collation modelBuilder.Entity(entity => diff --git a/Common/OpenShockDb/UserNameBlacklist.cs b/Common/OpenShockDb/UserNameBlacklist.cs index 9f344af1..b24a37b7 100644 --- a/Common/OpenShockDb/UserNameBlacklist.cs +++ b/Common/OpenShockDb/UserNameBlacklist.cs @@ -1,9 +1,13 @@ -namespace OpenShock.Common.OpenShockDb; +using NpgsqlTypes; +using OpenShock.Common.Utils; +namespace OpenShock.Common.OpenShockDb; + +[PgEnum] public enum MatchTypeEnum { - Exact, - Contains, + [PgName("exact")] Exact, + [PgName("contains")] Contains, } public sealed class UserNameBlacklist diff --git a/Common/Utils/PgEnumAttribute.cs b/Common/Utils/PgEnumAttribute.cs new file mode 100644 index 00000000..22a98327 --- /dev/null +++ b/Common/Utils/PgEnumAttribute.cs @@ -0,0 +1,16 @@ +namespace OpenShock.Common.Utils; + +/// +/// Marks an enum as a Postgres native enum type so it is automatically registered +/// with the EF Core model via . +/// +/// +/// Explicit Postgres type name. When omitted, the C# type name is converted to snake_case. +/// +/// Postgres schema; defaults to the model's default schema when null. +[AttributeUsage(AttributeTargets.Enum)] +public sealed class PgEnumAttribute(string? name = null, string? schema = null) : Attribute +{ + public string? Name { get; } = name; + public string? Schema { get; } = schema; +} From f57cda6ea5999789481a44cac6c139c84dc31e9e Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Jul 2026 01:43:18 +0200 Subject: [PATCH 2/5] refactor: make pg enum migration robust via type recreation Restamp RefactorPgEnums to run after AddEmailOutbox (resolving the merge ordering) and rewrite it to recreate each changed enum instead of relying on EF's annotation diff, which can only ALTER TYPE ... ADD VALUE and so silently no-ops reorders and leaves stale labels on renames. For each affected type the migration detaches its columns to text, drops the old type, remaps values, recreates the type, and reattaches: - control_type / password_encryption_type: reorder labels - shocker_model_type: rename labels to snake_case (cai_xianlin, petrainer, petrainer_998dr, wellturn_t330), fixing the petTrainer typo Verified both Up and Down round-trip against Postgres 15 with seeded data. --- .../20260612142230_RefactorPgEnums.cs | 66 -------------- ...0260630233834_RefactorPgEnums.Designer.cs} | 87 ++++++++++++++++++- .../20260630233834_RefactorPgEnums.cs | 77 ++++++++++++++++ .../OpenShockContextModelSnapshot.cs | 2 +- Common/Models/ShockerModelType.cs | 6 +- Common/Utils/PgEnumAttribute.cs | 2 +- 6 files changed, 166 insertions(+), 74 deletions(-) delete mode 100644 Common/Migrations/20260612142230_RefactorPgEnums.cs rename Common/Migrations/{20260612142230_RefactorPgEnums.Designer.cs => 20260630233834_RefactorPgEnums.Designer.cs} (94%) create mode 100644 Common/Migrations/20260630233834_RefactorPgEnums.cs diff --git a/Common/Migrations/20260612142230_RefactorPgEnums.cs b/Common/Migrations/20260612142230_RefactorPgEnums.cs deleted file mode 100644 index 7b83c2ae..00000000 --- a/Common/Migrations/20260612142230_RefactorPgEnums.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace OpenShock.Common.Migrations -{ - /// - public partial class RefactorPgEnums : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql("ALTER TYPE shocker_model_type RENAME VALUE 'petTrainer' TO 'petrainer';"); - - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False") - .Annotation("Npgsql:Enum:configuration_value_type", "string,bool,int,float,json") - .Annotation("Npgsql:Enum:control_limit_mode", "clamp,lerp") - .Annotation("Npgsql:Enum:control_type", "stop,shock,vibrate,sound") - .Annotation("Npgsql:Enum:match_type_enum", "exact,contains") - .Annotation("Npgsql:Enum:ota_update_status", "started,running,finished,error,timeout") - .Annotation("Npgsql:Enum:password_encryption_type", "bcrypt_enhanced,pbkdf2") - .Annotation("Npgsql:Enum:permission_type", "shockers.use,shockers.edit,shockers.pause,devices.edit,devices.auth") - .Annotation("Npgsql:Enum:role_type", "support,staff,admin,system") - .Annotation("Npgsql:Enum:shocker_model_type", "caiXianlin,petrainer,petrainer998DR,wellturnT330") - .OldAnnotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False") - .OldAnnotation("Npgsql:Enum:configuration_value_type", "string,bool,int,float,json") - .OldAnnotation("Npgsql:Enum:control_limit_mode", "clamp,lerp") - .OldAnnotation("Npgsql:Enum:control_type", "sound,vibrate,shock,stop") - .OldAnnotation("Npgsql:Enum:match_type_enum", "exact,contains") - .OldAnnotation("Npgsql:Enum:ota_update_status", "started,running,finished,error,timeout") - .OldAnnotation("Npgsql:Enum:password_encryption_type", "pbkdf2,bcrypt_enhanced") - .OldAnnotation("Npgsql:Enum:permission_type", "shockers.use,shockers.edit,shockers.pause,devices.edit,devices.auth") - .OldAnnotation("Npgsql:Enum:role_type", "support,staff,admin,system") - .OldAnnotation("Npgsql:Enum:shocker_model_type", "caiXianlin,petTrainer,petrainer998DR,wellturnT330"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False") - .Annotation("Npgsql:Enum:configuration_value_type", "string,bool,int,float,json") - .Annotation("Npgsql:Enum:control_limit_mode", "clamp,lerp") - .Annotation("Npgsql:Enum:control_type", "sound,vibrate,shock,stop") - .Annotation("Npgsql:Enum:match_type_enum", "exact,contains") - .Annotation("Npgsql:Enum:ota_update_status", "started,running,finished,error,timeout") - .Annotation("Npgsql:Enum:password_encryption_type", "pbkdf2,bcrypt_enhanced") - .Annotation("Npgsql:Enum:permission_type", "shockers.use,shockers.edit,shockers.pause,devices.edit,devices.auth") - .Annotation("Npgsql:Enum:role_type", "support,staff,admin,system") - .Annotation("Npgsql:Enum:shocker_model_type", "caiXianlin,petTrainer,petrainer998DR,wellturnT330") - .OldAnnotation("Npgsql:CollationDefinition:public.ndcoll", "und-u-ks-level2,und-u-ks-level2,icu,False") - .OldAnnotation("Npgsql:Enum:configuration_value_type", "string,bool,int,float,json") - .OldAnnotation("Npgsql:Enum:control_limit_mode", "clamp,lerp") - .OldAnnotation("Npgsql:Enum:control_type", "stop,shock,vibrate,sound") - .OldAnnotation("Npgsql:Enum:match_type_enum", "exact,contains") - .OldAnnotation("Npgsql:Enum:ota_update_status", "started,running,finished,error,timeout") - .OldAnnotation("Npgsql:Enum:password_encryption_type", "bcrypt_enhanced,pbkdf2") - .OldAnnotation("Npgsql:Enum:permission_type", "shockers.use,shockers.edit,shockers.pause,devices.edit,devices.auth") - .OldAnnotation("Npgsql:Enum:role_type", "support,staff,admin,system") - .OldAnnotation("Npgsql:Enum:shocker_model_type", "caiXianlin,petrainer,petrainer998DR,wellturnT330"); - - migrationBuilder.Sql("ALTER TYPE shocker_model_type RENAME VALUE 'petrainer' TO 'petTrainer';"); - } - } -} diff --git a/Common/Migrations/20260612142230_RefactorPgEnums.Designer.cs b/Common/Migrations/20260630233834_RefactorPgEnums.Designer.cs similarity index 94% rename from Common/Migrations/20260612142230_RefactorPgEnums.Designer.cs rename to Common/Migrations/20260630233834_RefactorPgEnums.Designer.cs index 1524c1ef..c88a0825 100644 --- a/Common/Migrations/20260612142230_RefactorPgEnums.Designer.cs +++ b/Common/Migrations/20260630233834_RefactorPgEnums.Designer.cs @@ -15,7 +15,7 @@ namespace OpenShock.Common.Migrations { [DbContext(typeof(MigrationOpenShockContext))] - [Migration("20260612142230_RefactorPgEnums")] + [Migration("20260630233834_RefactorPgEnums")] partial class RefactorPgEnums { /// @@ -24,18 +24,20 @@ 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", "10.0.8") + .HasAnnotation("ProductVersion", "10.0.9") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "configuration_value_type", new[] { "string", "bool", "int", "float", "json" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_limit_mode", new[] { "clamp", "lerp" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "control_type", new[] { "stop", "shock", "vibrate", "sound" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "email_status", new[] { "pending", "sending", "sent", "failed", "skipped" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "email_type", new[] { "account_activation", "password_reset", "email_verification", "email_change_notice" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "match_type_enum", new[] { "exact", "contains" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "ota_update_status", new[] { "started", "running", "finished", "error", "timeout" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "password_encryption_type", new[] { "bcrypt_enhanced", "pbkdf2" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "permission_type", new[] { "shockers.use", "shockers.edit", "shockers.pause", "devices.edit", "devices.auth" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "role_type", new[] { "support", "staff", "admin", "system" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "shocker_model_type", new[] { "caiXianlin", "petTrainer", "petrainer998DR", "wellturnT330" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "shocker_model_type", new[] { "cai_xianlin", "petrainer", "petrainer_998dr", "wellturn_t330" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => @@ -429,6 +431,85 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("discord_webhooks", (string)null); }); + modelBuilder.Entity("OpenShock.Common.OpenShockDb.EmailOutboxMessage", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AttemptCount") + .IsConcurrencyToken() + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("attempt_count"); + + b.Property("CoalesceKey") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("coalesce_key"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("FailedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("failed_at"); + + b.Property("LastError") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)") + .HasColumnName("last_error"); + + b.Property("NextAttemptAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("next_attempt_at") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property>("Payload") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("payload"); + + b.Property("Recipient") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)") + .HasColumnName("recipient"); + + b.Property("RecipientName") + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasColumnName("recipient_name"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("sent_at"); + + b.Property("Status") + .HasColumnType("email_status") + .HasColumnName("status"); + + b.Property("Type") + .HasColumnType("email_type") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("email_outbox_pkey"); + + b.HasIndex("CoalesceKey"); + + b.HasIndex("Recipient"); + + b.HasIndex("Status", "NextAttemptAt"); + + b.ToTable("email_outbox", (string)null); + }); + modelBuilder.Entity("OpenShock.Common.OpenShockDb.EmailProviderBlacklist", b => { b.Property("Id") diff --git a/Common/Migrations/20260630233834_RefactorPgEnums.cs b/Common/Migrations/20260630233834_RefactorPgEnums.cs new file mode 100644 index 00000000..50d211c5 --- /dev/null +++ b/Common/Migrations/20260630233834_RefactorPgEnums.cs @@ -0,0 +1,77 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OpenShock.Common.Migrations +{ + /// + public partial class RefactorPgEnums : Migration + { + // Postgres cannot reorder or remove labels of an existing enum type in place, and + // ALTER TYPE ... ADD VALUE is the only thing EF's annotation diff emits. So any change + // beyond appending a label silently no-ops (reorder) or leaves stale labels behind + // (rename). To apply these changes robustly we recreate each affected type: + // detach columns to text -> drop the old type -> (remap values) -> create the new type -> reattach. + // This works regardless of the current label set/order and is fully reversible. + + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // control_type: reorder labels to match the C# enum's declaration order. + migrationBuilder.Sql("ALTER TABLE shocker_control_logs ALTER COLUMN type TYPE text USING type::text;"); + migrationBuilder.Sql("DROP TYPE control_type;"); + migrationBuilder.Sql("CREATE TYPE control_type AS ENUM ('stop', 'shock', 'vibrate', 'sound');"); + migrationBuilder.Sql("ALTER TABLE shocker_control_logs ALTER COLUMN type TYPE control_type USING type::control_type;"); + + // password_encryption_type: reorder labels. Orphan type (no column maps to it). + migrationBuilder.Sql("DROP TYPE password_encryption_type;"); + migrationBuilder.Sql("CREATE TYPE password_encryption_type AS ENUM ('bcrypt_enhanced', 'pbkdf2');"); + + // shocker_model_type: rename labels to snake_case (and fix the petTrainer typo). + migrationBuilder.Sql("ALTER TABLE shockers ALTER COLUMN model TYPE text USING model::text;"); + migrationBuilder.Sql("DROP TYPE shocker_model_type;"); + migrationBuilder.Sql( + """ + UPDATE shockers SET model = CASE model + WHEN 'caiXianlin' THEN 'cai_xianlin' + WHEN 'petTrainer' THEN 'petrainer' + WHEN 'petrainer998DR' THEN 'petrainer_998dr' + WHEN 'wellturnT330' THEN 'wellturn_t330' + ELSE model + END; + """); + migrationBuilder.Sql("CREATE TYPE shocker_model_type AS ENUM ('cai_xianlin', 'petrainer', 'petrainer_998dr', 'wellturn_t330');"); + migrationBuilder.Sql("ALTER TABLE shockers ALTER COLUMN model TYPE shocker_model_type USING model::shocker_model_type;"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + // control_type: restore the original label order. + migrationBuilder.Sql("ALTER TABLE shocker_control_logs ALTER COLUMN type TYPE text USING type::text;"); + migrationBuilder.Sql("DROP TYPE control_type;"); + migrationBuilder.Sql("CREATE TYPE control_type AS ENUM ('sound', 'vibrate', 'shock', 'stop');"); + migrationBuilder.Sql("ALTER TABLE shocker_control_logs ALTER COLUMN type TYPE control_type USING type::control_type;"); + + // password_encryption_type: restore the original label order. + migrationBuilder.Sql("DROP TYPE password_encryption_type;"); + migrationBuilder.Sql("CREATE TYPE password_encryption_type AS ENUM ('pbkdf2', 'bcrypt_enhanced');"); + + // shocker_model_type: restore the original camelCase labels. + migrationBuilder.Sql("ALTER TABLE shockers ALTER COLUMN model TYPE text USING model::text;"); + migrationBuilder.Sql("DROP TYPE shocker_model_type;"); + migrationBuilder.Sql( + """ + UPDATE shockers SET model = CASE model + WHEN 'cai_xianlin' THEN 'caiXianlin' + WHEN 'petrainer' THEN 'petTrainer' + WHEN 'petrainer_998dr' THEN 'petrainer998DR' + WHEN 'wellturn_t330' THEN 'wellturnT330' + ELSE model + END; + """); + migrationBuilder.Sql("CREATE TYPE shocker_model_type AS ENUM ('caiXianlin', 'petTrainer', 'petrainer998DR', 'wellturnT330');"); + migrationBuilder.Sql("ALTER TABLE shockers ALTER COLUMN model TYPE shocker_model_type USING model::shocker_model_type;"); + } + } +} diff --git a/Common/Migrations/OpenShockContextModelSnapshot.cs b/Common/Migrations/OpenShockContextModelSnapshot.cs index 2e253276..65c2be7a 100644 --- a/Common/Migrations/OpenShockContextModelSnapshot.cs +++ b/Common/Migrations/OpenShockContextModelSnapshot.cs @@ -34,7 +34,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "password_encryption_type", new[] { "bcrypt_enhanced", "pbkdf2" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "permission_type", new[] { "shockers.use", "shockers.edit", "shockers.pause", "devices.edit", "devices.auth" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "role_type", new[] { "support", "staff", "admin", "system" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "shocker_model_type", new[] { "caiXianlin", "petrainer", "petrainer998DR", "wellturnT330" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "shocker_model_type", new[] { "cai_xianlin", "petrainer", "petrainer_998dr", "wellturn_t330" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => diff --git a/Common/Models/ShockerModelType.cs b/Common/Models/ShockerModelType.cs index b1eb8cb3..8247d439 100644 --- a/Common/Models/ShockerModelType.cs +++ b/Common/Models/ShockerModelType.cs @@ -6,8 +6,8 @@ namespace OpenShock.Common.Models; [PgEnum] public enum ShockerModelType { - [PgName("caiXianlin")] CaiXianlin = 0, + [PgName("cai_xianlin")] CaiXianlin = 0, [PgName("petrainer")] PetTrainer = 1, - [PgName("petrainer998DR")] Petrainer998DR = 2, - [PgName("wellturnT330")] WellturnT330 = 3, + [PgName("petrainer_998dr")] Petrainer998DR = 2, + [PgName("wellturn_t330")] WellturnT330 = 3, } \ No newline at end of file diff --git a/Common/Utils/PgEnumAttribute.cs b/Common/Utils/PgEnumAttribute.cs index 22a98327..c72a30aa 100644 --- a/Common/Utils/PgEnumAttribute.cs +++ b/Common/Utils/PgEnumAttribute.cs @@ -2,7 +2,7 @@ namespace OpenShock.Common.Utils; /// /// Marks an enum as a Postgres native enum type so it is automatically registered -/// with the EF Core model via . +/// with the EF Core model via . /// /// /// Explicit Postgres type name. When omitted, the C# type name is converted to snake_case. From 99bfda99944f27c287b6b592c5de022626cfc20f Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Jul 2026 15:39:40 +0200 Subject: [PATCH 3/5] refactor(db): reorganize OpenShockDb into subfolders and consolidate enums Move entity classes into OpenShockDb/Models, key helpers into Keys/, queries into Queries/, and NpgsqlEnumExtensions into Extensions/. Relocate domain enums from Common/Models into OpenShockDb/Enums, add the missing DB enums (ConfigurationValueType, MatchTypeEnum, OtaUpdateStatus, PermissionType, RoleType), and split enum helper methods into dedicated *Extensions classes. Update all consumers to the new namespaces. --- API.IntegrationTests/Helpers/TestHelper.cs | 4 +-- API.IntegrationTests/Tests/TokensTests.cs | 2 +- .../Account/Authenticated/ChangeUsername.cs | 1 + API/Controller/Devices/DevicesController.cs | 1 + API/Controller/Sessions/DeleteSessions.cs | 1 + .../Shares/UserShares/UpdateShockerShares.cs | 1 + API/Controller/Shockers/EditShocker.cs | 1 + API/Controller/Shockers/PauseShocker.cs | 1 + API/Controller/Shockers/RemoveShocker.cs | 1 + API/Controller/Users/GetSelf.cs | 1 + API/Models/Requests/EditTokenRequest.cs | 1 + API/Models/Requests/EditTokenRequestV2.cs | 1 + API/Models/Requests/NewShocker.cs | 1 + API/Models/Response/LogEntry.cs | 1 + API/Models/Response/LogEntryWithHub.cs | 1 + API/Models/Response/ShockerResponse.cs | 1 + API/Models/Response/TokenCreatedResponse.cs | 1 + API/Models/Response/TokenCreatedResponseV2.cs | 1 + API/Models/Response/TokenResponse.cs | 1 + API/Models/Response/TokenResponseV2.cs | 1 + .../Models/ApiTokenControlLimitsTests.cs | 1 + Common.Tests/Utils/HashingUtilsTests.cs | 1 + Common.Tests/Utils/PermissionUtilsTests.cs | 1 + .../ApiTokenPermissionRequirement.cs | 1 + Common/DeviceControl/ControlShockerObj.cs | 1 + Common/Errors/AuthorizationError.cs | 1 + .../Extensions/ClaimsPrincipalExtensions.cs | 1 + .../PermissionTypeConverter.cs | 1 + ...0260603134636_AddApiTokenShockerControl.cs | 1 + .../20260630210423_AddEmailOutbox.cs | 1 + Common/Models/OtaUpdateStatus.cs | 14 ---------- ...ionType.cs => PermissionTypeExtensions.cs} | 21 ++------------- Common/Models/RoleType.cs | 13 ---------- Common/Models/Services/Ota/OtaItem.cs | 1 + Common/Models/WebSocket/ControlResponse.cs | 4 ++- .../Models/WebSocket/LCG/ClientLiveFrame.cs | 1 + Common/Models/WebSocket/User/Control.cs | 1 + Common/Models/WebSocket/User/ControlLog.cs | 1 + .../Enums/ConfigurationValueType.cs | 14 ++++++++++ .../Enums}/ControlLimitMode.cs | 6 ++--- .../Enums}/ControlType.cs | 6 ++--- .../Enums}/EmailStatus.cs | 26 +++++++++---------- .../Enums}/EmailType.cs | 14 +++++----- Common/OpenShockDb/Enums/MatchTypeEnum.cs | 11 ++++++++ Common/OpenShockDb/Enums/OtaUpdateStatus.cs | 14 ++++++++++ .../Enums}/PasswordHashingAlgorithm.cs | 9 ++++--- Common/OpenShockDb/Enums/PermissionType.cs | 23 ++++++++++++++++ Common/OpenShockDb/Enums/RoleType.cs | 13 ++++++++++ .../Enums}/ShockerModelType.cs | 8 +++--- .../{ => Extensions}/NpgsqlEnumExtensions.cs | 12 ++------- .../{ => Keys}/EmailOutboxCoalesceKeys.cs | 2 +- .../{ => Keys}/EmailOutboxPayloadKeys.cs | 9 ++++--- .../{ => Models}/AdminUsersView.cs | 3 +-- Common/OpenShockDb/{ => Models}/ApiToken.cs | 1 - .../{ => Models}/ApiTokenReport.cs | 0 .../{ => Models}/ConfigurationItem.cs | 13 ---------- Common/OpenShockDb/{ => Models}/Device.cs | 0 .../{ => Models}/DeviceOtaUpdate.cs | 4 +-- .../{ => Models}/DiscordWebhook.cs | 0 .../{ => Models}/EmailOutboxMessage.cs | 2 -- .../{ => Models}/EmailProviderBlacklist.cs | 0 .../OpenShockDb/{ => Models}/PublicShare.cs | 0 .../{ => Models}/PublicShareShocker.cs | 0 .../{ => Models}/SafetySettings.cs | 0 Common/OpenShockDb/{ => Models}/Shocker.cs | 4 +-- .../{ => Models}/ShockerControlLog.cs | 4 +-- .../{ => Models}/ShockerShareCode.cs | 0 Common/OpenShockDb/{ => Models}/User.cs | 4 +-- .../{ => Models}/UserActivationRequest.cs | 0 .../{ => Models}/UserDeactivation.cs | 0 .../{ => Models}/UserEmailChange.cs | 0 .../{ => Models}/UserNameBlacklist.cs | 10 ------- .../{ => Models}/UserNameChange.cs | 0 .../{ => Models}/UserOAuthConnection.cs | 0 .../{ => Models}/UserPasswordReset.cs | 0 Common/OpenShockDb/{ => Models}/UserShare.cs | 0 .../{ => Models}/UserShareInvite.cs | 0 .../{ => Models}/UserShareInviteShocker.cs | 0 .../{ => Queries}/EmailOutboxQueries.cs | 1 - .../CustomProblems/TokenPermissionProblem.cs | 1 + Common/Redis/PubSub/DeviceMessage.cs | 1 + Common/Utils/HashingUtils.cs | 1 + Common/Utils/PermissionUtils.cs | 1 + Common/Utils/PgEnumAttribute.cs | 13 +++++----- .../Email/Outbox/EmailOutboxRetryPolicy.cs | 2 +- .../Controllers/HubV1Controller.cs | 1 + .../Controllers/HubV2Controller.cs | 1 + .../LifetimeManager/ShockerState.cs | 1 + LiveControlGateway/Mappers/FbsMapper.cs | 11 ++++---- 89 files changed, 183 insertions(+), 153 deletions(-) delete mode 100644 Common/Models/OtaUpdateStatus.cs rename Common/Models/{PermissionType.cs => PermissionTypeExtensions.cs} (85%) delete mode 100644 Common/Models/RoleType.cs create mode 100644 Common/OpenShockDb/Enums/ConfigurationValueType.cs rename Common/{Models => OpenShockDb/Enums}/ControlLimitMode.cs (79%) rename Common/{Models => OpenShockDb/Enums}/ControlType.cs (63%) rename Common/{Models => OpenShockDb/Enums}/EmailStatus.cs (71%) rename Common/{Models => OpenShockDb/Enums}/EmailType.cs (64%) create mode 100644 Common/OpenShockDb/Enums/MatchTypeEnum.cs create mode 100644 Common/OpenShockDb/Enums/OtaUpdateStatus.cs rename Common/{Models => OpenShockDb/Enums}/PasswordHashingAlgorithm.cs (61%) create mode 100644 Common/OpenShockDb/Enums/PermissionType.cs create mode 100644 Common/OpenShockDb/Enums/RoleType.cs rename Common/{Models => OpenShockDb/Enums}/ShockerModelType.cs (58%) rename Common/OpenShockDb/{ => Extensions}/NpgsqlEnumExtensions.cs (82%) rename Common/OpenShockDb/{ => Keys}/EmailOutboxCoalesceKeys.cs (93%) rename Common/OpenShockDb/{ => Keys}/EmailOutboxPayloadKeys.cs (78%) rename Common/OpenShockDb/{ => Models}/AdminUsersView.cs (93%) rename Common/OpenShockDb/{ => Models}/ApiToken.cs (97%) rename Common/OpenShockDb/{ => Models}/ApiTokenReport.cs (100%) rename Common/OpenShockDb/{ => Models}/ConfigurationItem.cs (61%) rename Common/OpenShockDb/{ => Models}/Device.cs (100%) rename Common/OpenShockDb/{ => Models}/DeviceOtaUpdate.cs (84%) rename Common/OpenShockDb/{ => Models}/DiscordWebhook.cs (100%) rename Common/OpenShockDb/{ => Models}/EmailOutboxMessage.cs (99%) rename Common/OpenShockDb/{ => Models}/EmailProviderBlacklist.cs (100%) rename Common/OpenShockDb/{ => Models}/PublicShare.cs (100%) rename Common/OpenShockDb/{ => Models}/PublicShareShocker.cs (100%) rename Common/OpenShockDb/{ => Models}/SafetySettings.cs (100%) rename Common/OpenShockDb/{ => Models}/Shocker.cs (91%) rename Common/OpenShockDb/{ => Models}/ShockerControlLog.cs (89%) rename Common/OpenShockDb/{ => Models}/ShockerShareCode.cs (100%) rename Common/OpenShockDb/{ => Models}/User.cs (96%) rename Common/OpenShockDb/{ => Models}/UserActivationRequest.cs (100%) rename Common/OpenShockDb/{ => Models}/UserDeactivation.cs (100%) rename Common/OpenShockDb/{ => Models}/UserEmailChange.cs (100%) rename Common/OpenShockDb/{ => Models}/UserNameBlacklist.cs (78%) rename Common/OpenShockDb/{ => Models}/UserNameChange.cs (100%) rename Common/OpenShockDb/{ => Models}/UserOAuthConnection.cs (100%) rename Common/OpenShockDb/{ => Models}/UserPasswordReset.cs (100%) rename Common/OpenShockDb/{ => Models}/UserShare.cs (100%) rename Common/OpenShockDb/{ => Models}/UserShareInvite.cs (100%) rename Common/OpenShockDb/{ => Models}/UserShareInviteShocker.cs (100%) rename Common/OpenShockDb/{ => Queries}/EmailOutboxQueries.cs (98%) diff --git a/API.IntegrationTests/Helpers/TestHelper.cs b/API.IntegrationTests/Helpers/TestHelper.cs index 71947eb2..f71f5608 100644 --- a/API.IntegrationTests/Helpers/TestHelper.cs +++ b/API.IntegrationTests/Helpers/TestHelper.cs @@ -146,7 +146,7 @@ public static async Task CreateUserInDb( WebApplicationFactory factory, Guid userId, string name = "TestToken", - List? permissions = null) + List? permissions = null) { await using var scope = factory.Services.CreateAsyncScope(); var db = scope.ServiceProvider.GetRequiredService(); @@ -160,7 +160,7 @@ public static async Task CreateUserInDb( Name = name, TokenHash = HashingUtils.HashToken(rawToken), CreatedByIp = IPAddress.Loopback, - Permissions = permissions ?? [Common.Models.PermissionType.Shockers_Use] + Permissions = permissions ?? [PermissionType.Shockers_Use] }); await db.SaveChangesAsync(); return (tokenId, rawToken); diff --git a/API.IntegrationTests/Tests/TokensTests.cs b/API.IntegrationTests/Tests/TokensTests.cs index ef01677b..ca022254 100644 --- a/API.IntegrationTests/Tests/TokensTests.cs +++ b/API.IntegrationTests/Tests/TokensTests.cs @@ -348,7 +348,7 @@ public async Task ApiTokenAuth_CanAccessDevices() { var userId = await TestHelper.CreateUserInDb(WebApplicationFactory, "tokauth", "tokauth@test.org", "SecurePassword123#"); var (_, rawToken) = await TestHelper.CreateApiTokenInDb(WebApplicationFactory, userId, "AuthToken", - [Common.Models.PermissionType.Shockers_Use, Common.Models.PermissionType.Devices_Edit]); + [Common.OpenShockDb.PermissionType.Shockers_Use, Common.OpenShockDb.PermissionType.Devices_Edit]); using var client = TestHelper.CreateApiTokenClient(WebApplicationFactory, rawToken); var response = await client.GetAsync("/1/devices"); diff --git a/API/Controller/Account/Authenticated/ChangeUsername.cs b/API/Controller/Account/Authenticated/ChangeUsername.cs index 759fa11c..28b343ec 100644 --- a/API/Controller/Account/Authenticated/ChangeUsername.cs +++ b/API/Controller/Account/Authenticated/ChangeUsername.cs @@ -4,6 +4,7 @@ using OpenShock.Common.Models; using OpenShock.Common.Problems; using System.Net.Mime; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Controller.Account.Authenticated; diff --git a/API/Controller/Devices/DevicesController.cs b/API/Controller/Devices/DevicesController.cs index 7320d064..335734b5 100644 --- a/API/Controller/Devices/DevicesController.cs +++ b/API/Controller/Devices/DevicesController.cs @@ -13,6 +13,7 @@ using System.Net.Mime; using System.Security.Cryptography; using OpenShock.API.Services.DeviceUpdate; +using OpenShock.Common.OpenShockDb; using Redis.OM; namespace OpenShock.API.Controller.Devices; diff --git a/API/Controller/Sessions/DeleteSessions.cs b/API/Controller/Sessions/DeleteSessions.cs index 74c5b87c..b84f0e43 100644 --- a/API/Controller/Sessions/DeleteSessions.cs +++ b/API/Controller/Sessions/DeleteSessions.cs @@ -4,6 +4,7 @@ using OpenShock.Common.Models; using OpenShock.Common.Problems; using System.Net.Mime; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Controller.Sessions; diff --git a/API/Controller/Shares/UserShares/UpdateShockerShares.cs b/API/Controller/Shares/UserShares/UpdateShockerShares.cs index 0d9037ab..cb6d21ab 100644 --- a/API/Controller/Shares/UserShares/UpdateShockerShares.cs +++ b/API/Controller/Shares/UserShares/UpdateShockerShares.cs @@ -7,6 +7,7 @@ using OpenShock.Common.Authentication.Attributes; using OpenShock.Common.Errors; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Problems; namespace OpenShock.API.Controller.Shares.UserShares; diff --git a/API/Controller/Shockers/EditShocker.cs b/API/Controller/Shockers/EditShocker.cs index 42b3a934..6cafad77 100644 --- a/API/Controller/Shockers/EditShocker.cs +++ b/API/Controller/Shockers/EditShocker.cs @@ -7,6 +7,7 @@ using OpenShock.Common.Authentication.Attributes; using OpenShock.Common.Errors; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Problems; namespace OpenShock.API.Controller.Shockers; diff --git a/API/Controller/Shockers/PauseShocker.cs b/API/Controller/Shockers/PauseShocker.cs index 9c61a214..257c2d5d 100644 --- a/API/Controller/Shockers/PauseShocker.cs +++ b/API/Controller/Shockers/PauseShocker.cs @@ -7,6 +7,7 @@ using OpenShock.Common.Authentication.Attributes; using OpenShock.Common.Errors; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Problems; namespace OpenShock.API.Controller.Shockers; diff --git a/API/Controller/Shockers/RemoveShocker.cs b/API/Controller/Shockers/RemoveShocker.cs index 8aa9438a..6cf852b9 100644 --- a/API/Controller/Shockers/RemoveShocker.cs +++ b/API/Controller/Shockers/RemoveShocker.cs @@ -7,6 +7,7 @@ using OpenShock.Common.Errors; using OpenShock.Common.Extensions; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Problems; namespace OpenShock.API.Controller.Shockers; diff --git a/API/Controller/Users/GetSelf.cs b/API/Controller/Users/GetSelf.cs index 396f2c41..b3a3ced8 100644 --- a/API/Controller/Users/GetSelf.cs +++ b/API/Controller/Users/GetSelf.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using OpenShock.Common.Extensions; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Controller.Users; diff --git a/API/Models/Requests/EditTokenRequest.cs b/API/Models/Requests/EditTokenRequest.cs index fd64005a..afd12afc 100644 --- a/API/Models/Requests/EditTokenRequest.cs +++ b/API/Models/Requests/EditTokenRequest.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Requests; diff --git a/API/Models/Requests/EditTokenRequestV2.cs b/API/Models/Requests/EditTokenRequestV2.cs index fb50f456..0f738009 100644 --- a/API/Models/Requests/EditTokenRequestV2.cs +++ b/API/Models/Requests/EditTokenRequestV2.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Requests; diff --git a/API/Models/Requests/NewShocker.cs b/API/Models/Requests/NewShocker.cs index 3b1f0be2..7c4c7658 100644 --- a/API/Models/Requests/NewShocker.cs +++ b/API/Models/Requests/NewShocker.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Requests; diff --git a/API/Models/Response/LogEntry.cs b/API/Models/Response/LogEntry.cs index 585b8068..0bfdc0db 100644 --- a/API/Models/Response/LogEntry.cs +++ b/API/Models/Response/LogEntry.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/LogEntryWithHub.cs b/API/Models/Response/LogEntryWithHub.cs index d429b73b..82e91920 100644 --- a/API/Models/Response/LogEntryWithHub.cs +++ b/API/Models/Response/LogEntryWithHub.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/ShockerResponse.cs b/API/Models/Response/ShockerResponse.cs index 6b0e5bac..2df2d52f 100644 --- a/API/Models/Response/ShockerResponse.cs +++ b/API/Models/Response/ShockerResponse.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/TokenCreatedResponse.cs b/API/Models/Response/TokenCreatedResponse.cs index e2fe87c9..016b0bb8 100644 --- a/API/Models/Response/TokenCreatedResponse.cs +++ b/API/Models/Response/TokenCreatedResponse.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/TokenCreatedResponseV2.cs b/API/Models/Response/TokenCreatedResponseV2.cs index 9ee5ca3b..04b34ef4 100644 --- a/API/Models/Response/TokenCreatedResponseV2.cs +++ b/API/Models/Response/TokenCreatedResponseV2.cs @@ -1,5 +1,6 @@ using OpenShock.API.Models; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/TokenResponse.cs b/API/Models/Response/TokenResponse.cs index 73676b0e..eb9ef60e 100644 --- a/API/Models/Response/TokenResponse.cs +++ b/API/Models/Response/TokenResponse.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/TokenResponseV2.cs b/API/Models/Response/TokenResponseV2.cs index 9256b575..cf7829ba 100644 --- a/API/Models/Response/TokenResponseV2.cs +++ b/API/Models/Response/TokenResponseV2.cs @@ -1,5 +1,6 @@ using OpenShock.API.Models; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/Common.Tests/Models/ApiTokenControlLimitsTests.cs b/Common.Tests/Models/ApiTokenControlLimitsTests.cs index 6d83d69d..cdf3436a 100644 --- a/Common.Tests/Models/ApiTokenControlLimitsTests.cs +++ b/Common.Tests/Models/ApiTokenControlLimitsTests.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Tests.Models; diff --git a/Common.Tests/Utils/HashingUtilsTests.cs b/Common.Tests/Utils/HashingUtilsTests.cs index 698a2a0e..fbb3b760 100644 --- a/Common.Tests/Utils/HashingUtilsTests.cs +++ b/Common.Tests/Utils/HashingUtilsTests.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Utils; namespace OpenShock.Common.Tests.Utils; diff --git a/Common.Tests/Utils/PermissionUtilsTests.cs b/Common.Tests/Utils/PermissionUtilsTests.cs index de808de2..218f5e1a 100644 --- a/Common.Tests/Utils/PermissionUtilsTests.cs +++ b/Common.Tests/Utils/PermissionUtilsTests.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Utils; namespace OpenShock.Common.Tests.Utils; diff --git a/Common/Authentication/Requirements/ApiTokenPermissionRequirement.cs b/Common/Authentication/Requirements/ApiTokenPermissionRequirement.cs index 275d565f..ef008e81 100644 --- a/Common/Authentication/Requirements/ApiTokenPermissionRequirement.cs +++ b/Common/Authentication/Requirements/ApiTokenPermissionRequirement.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authorization; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Authentication.Requirements; diff --git a/Common/DeviceControl/ControlShockerObj.cs b/Common/DeviceControl/ControlShockerObj.cs index fd94132f..6ddba91c 100644 --- a/Common/DeviceControl/ControlShockerObj.cs +++ b/Common/DeviceControl/ControlShockerObj.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.DeviceControl; diff --git a/Common/Errors/AuthorizationError.cs b/Common/Errors/AuthorizationError.cs index fb4d8ef2..d78e868b 100644 --- a/Common/Errors/AuthorizationError.cs +++ b/Common/Errors/AuthorizationError.cs @@ -1,5 +1,6 @@ using System.Net; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Problems; using OpenShock.Common.Problems.CustomProblems; diff --git a/Common/Extensions/ClaimsPrincipalExtensions.cs b/Common/Extensions/ClaimsPrincipalExtensions.cs index 1cabfdce..115d0bd9 100644 --- a/Common/Extensions/ClaimsPrincipalExtensions.cs +++ b/Common/Extensions/ClaimsPrincipalExtensions.cs @@ -1,6 +1,7 @@ using System.Security.Claims; using OpenShock.Common.Authentication; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Extensions; diff --git a/Common/JsonSerialization/PermissionTypeConverter.cs b/Common/JsonSerialization/PermissionTypeConverter.cs index d8ceb7b3..74445584 100644 --- a/Common/JsonSerialization/PermissionTypeConverter.cs +++ b/Common/JsonSerialization/PermissionTypeConverter.cs @@ -1,6 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.JsonSerialization; diff --git a/Common/Migrations/20260603134636_AddApiTokenShockerControl.cs b/Common/Migrations/20260603134636_AddApiTokenShockerControl.cs index d78e72a2..05c6a692 100644 --- a/Common/Migrations/20260603134636_AddApiTokenShockerControl.cs +++ b/Common/Migrations/20260603134636_AddApiTokenShockerControl.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore.Migrations; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; #nullable disable diff --git a/Common/Migrations/20260630210423_AddEmailOutbox.cs b/Common/Migrations/20260630210423_AddEmailOutbox.cs index 8f0ca811..a2e98620 100644 --- a/Common/Migrations/20260630210423_AddEmailOutbox.cs +++ b/Common/Migrations/20260630210423_AddEmailOutbox.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; #nullable disable diff --git a/Common/Models/OtaUpdateStatus.cs b/Common/Models/OtaUpdateStatus.cs deleted file mode 100644 index 9ed2e899..00000000 --- a/Common/Models/OtaUpdateStatus.cs +++ /dev/null @@ -1,14 +0,0 @@ -using NpgsqlTypes; -using OpenShock.Common.Utils; - -namespace OpenShock.Common.Models; - -[PgEnum] -public enum OtaUpdateStatus -{ - [PgName("started")] Started, - [PgName("running")] Running, - [PgName("finished")] Finished, - [PgName("error")] Error, - [PgName("timeout")] Timeout, -} diff --git a/Common/Models/PermissionType.cs b/Common/Models/PermissionTypeExtensions.cs similarity index 85% rename from Common/Models/PermissionType.cs rename to Common/Models/PermissionTypeExtensions.cs index 0219b10e..d09bbf98 100644 --- a/Common/Models/PermissionType.cs +++ b/Common/Models/PermissionTypeExtensions.cs @@ -1,28 +1,11 @@ -using System.Reflection; -using System.Text.Json.Serialization; +using System.Reflection; using NpgsqlTypes; -using OpenShock.Common.JsonSerialization; -using OpenShock.Common.Utils; +using OpenShock.Common.OpenShockDb; // ReSharper disable InconsistentNaming namespace OpenShock.Common.Models; -[PgEnum] -[JsonConverter(typeof(PermissionTypeConverter))] -public enum PermissionType -{ - [PgName("shockers.use")] Shockers_Use, - - [PgName("shockers.edit")] Shockers_Edit, - - [PgName("shockers.pause")] Shockers_Pause, - - [PgName("devices.edit")] Devices_Edit, - - [PgName("devices.auth")] Devices_Auth -} - public static class PermissionTypeExtensions { public static bool IsAllowed(this PermissionType permissionType, IReadOnlyList permissions) => diff --git a/Common/Models/RoleType.cs b/Common/Models/RoleType.cs deleted file mode 100644 index 03ccc153..00000000 --- a/Common/Models/RoleType.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NpgsqlTypes; -using OpenShock.Common.Utils; - -namespace OpenShock.Common.Models; - -[PgEnum] -public enum RoleType -{ - [PgName("support")] Support, - [PgName("staff")] Staff, - [PgName("admin")] Admin, - [PgName("system")] System, -} diff --git a/Common/Models/Services/Ota/OtaItem.cs b/Common/Models/Services/Ota/OtaItem.cs index 08590594..de1b4a68 100644 --- a/Common/Models/Services/Ota/OtaItem.cs +++ b/Common/Models/Services/Ota/OtaItem.cs @@ -1,5 +1,6 @@ using System.Text.Json.Serialization; using OpenShock.Common.JsonSerialization; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Models.Services.Ota; diff --git a/Common/Models/WebSocket/ControlResponse.cs b/Common/Models/WebSocket/ControlResponse.cs index f5d0bcf8..d9d87782 100644 --- a/Common/Models/WebSocket/ControlResponse.cs +++ b/Common/Models/WebSocket/ControlResponse.cs @@ -1,4 +1,6 @@ -namespace OpenShock.Common.Models.WebSocket; +using OpenShock.Common.OpenShockDb; + +namespace OpenShock.Common.Models.WebSocket; public sealed class ControlResponse { diff --git a/Common/Models/WebSocket/LCG/ClientLiveFrame.cs b/Common/Models/WebSocket/LCG/ClientLiveFrame.cs index 35a4ec12..ae3f336e 100644 --- a/Common/Models/WebSocket/LCG/ClientLiveFrame.cs +++ b/Common/Models/WebSocket/LCG/ClientLiveFrame.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Models.WebSocket.LCG; diff --git a/Common/Models/WebSocket/User/Control.cs b/Common/Models/WebSocket/User/Control.cs index 9400133c..21e3b32f 100644 --- a/Common/Models/WebSocket/User/Control.cs +++ b/Common/Models/WebSocket/User/Control.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Models.WebSocket.User; diff --git a/Common/Models/WebSocket/User/ControlLog.cs b/Common/Models/WebSocket/User/ControlLog.cs index 424f9adc..3e06b99a 100644 --- a/Common/Models/WebSocket/User/ControlLog.cs +++ b/Common/Models/WebSocket/User/ControlLog.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Models.WebSocket.User; diff --git a/Common/OpenShockDb/Enums/ConfigurationValueType.cs b/Common/OpenShockDb/Enums/ConfigurationValueType.cs new file mode 100644 index 00000000..d53ce7a7 --- /dev/null +++ b/Common/OpenShockDb/Enums/ConfigurationValueType.cs @@ -0,0 +1,14 @@ +using NpgsqlTypes; +using OpenShock.Common.Utils; + +namespace OpenShock.Common.OpenShockDb; + +[PgEnum(Name = "configuration_value_type")] +public enum ConfigurationValueType +{ + [PgName("string")] String = 0, + [PgName("bool")] Bool = 1, + [PgName("int")] Int = 2, + [PgName("float")] Float = 3, + [PgName("json")] Json = 4 +} diff --git a/Common/Models/ControlLimitMode.cs b/Common/OpenShockDb/Enums/ControlLimitMode.cs similarity index 79% rename from Common/Models/ControlLimitMode.cs rename to Common/OpenShockDb/Enums/ControlLimitMode.cs index bf4b48ba..e921a55b 100644 --- a/Common/Models/ControlLimitMode.cs +++ b/Common/OpenShockDb/Enums/ControlLimitMode.cs @@ -1,12 +1,12 @@ using NpgsqlTypes; using OpenShock.Common.Utils; -namespace OpenShock.Common.Models; +namespace OpenShock.Common.OpenShockDb; /// /// Determines how a per-token min/max limit is applied to an incoming control value. /// -[PgEnum] +[PgEnum(Name = "control_limit_mode")] public enum ControlLimitMode { /// @@ -17,5 +17,5 @@ public enum ControlLimitMode /// /// Linearly remap the full input range onto [min, max]. /// - [PgName("lerp")] Lerp = 1, + [PgName("lerp")] Lerp = 1 } diff --git a/Common/Models/ControlType.cs b/Common/OpenShockDb/Enums/ControlType.cs similarity index 63% rename from Common/Models/ControlType.cs rename to Common/OpenShockDb/Enums/ControlType.cs index 02a29243..717bfec6 100644 --- a/Common/Models/ControlType.cs +++ b/Common/OpenShockDb/Enums/ControlType.cs @@ -1,13 +1,13 @@ using NpgsqlTypes; using OpenShock.Common.Utils; -namespace OpenShock.Common.Models; +namespace OpenShock.Common.OpenShockDb; -[PgEnum] +[PgEnum(Name = "control_type")] public enum ControlType { [PgName("stop")] Stop = 0, [PgName("shock")] Shock = 1, [PgName("vibrate")] Vibrate = 2, - [PgName("sound")] Sound = 3, + [PgName("sound")] Sound = 3 } diff --git a/Common/Models/EmailStatus.cs b/Common/OpenShockDb/Enums/EmailStatus.cs similarity index 71% rename from Common/Models/EmailStatus.cs rename to Common/OpenShockDb/Enums/EmailStatus.cs index bff7f6ce..4ed0beec 100644 --- a/Common/Models/EmailStatus.cs +++ b/Common/OpenShockDb/Enums/EmailStatus.cs @@ -1,10 +1,10 @@ using NpgsqlTypes; using OpenShock.Common.Utils; -namespace OpenShock.Common.Models; +namespace OpenShock.Common.OpenShockDb; /// -/// Delivery state of an . +/// Delivery state of an . /// /// /// The row is written by the API as in the same transaction as the business @@ -12,38 +12,38 @@ namespace OpenShock.Common.Models; /// under a lease), hands it to the email provider, and records the outcome /// directly on the row - a terminal , , or , /// or back to with a future retry time. Retry count and scheduling live in the -/// row itself ( / -/// ), so the table is the complete, queryable +/// row itself ( / +/// ), so the table is the complete, queryable /// source of truth for every email's delivery state. /// -[PgEnum] +[PgEnum(Name = "email_status")] public enum EmailStatus { /// /// Due for delivery: either freshly enqueued, or a transient failure scheduled for a later retry - /// (distinguished by being in the - /// future and > 0). A row is eligible + /// (distinguished by being in the + /// future and > 0). A row is eligible /// to be claimed once its next-attempt time has passed. /// - [PgName("pending")] Pending, + [PgName("pending")] Pending = 0, /// /// Claimed by an executor and currently being delivered. The claim carries a lease - /// ( set to the lease expiry); if the + /// ( set to the lease expiry); if the /// process dies mid-send, the lease lapses and the row is reclaimed - so a crash cannot strand a /// message in this state. /// - [PgName("sending")] Sending, + [PgName("sending")] Sending = 1, /// The message was handed to the email provider successfully. Terminal. - [PgName("sent")] Sent, + [PgName("sent")] Sent = 2, /// /// Delivery was abandoned: it exhausted its retry budget or hit a permanent provider error. The /// row is kept (never auto-deleted) so it stays inspectable and can be requeued by an operator. /// Terminal. /// - [PgName("failed")] Failed, + [PgName("failed")] Failed = 3, /// /// The email was intentionally not sent because the underlying request no longer needs it (the @@ -51,5 +51,5 @@ public enum EmailStatus /// is a successful no-op, kept distinct from so operators never mistake it for /// a delivery problem or requeue it. Terminal. /// - [PgName("skipped")] Skipped + [PgName("skipped")] Skipped = 4 } diff --git a/Common/Models/EmailType.cs b/Common/OpenShockDb/Enums/EmailType.cs similarity index 64% rename from Common/Models/EmailType.cs rename to Common/OpenShockDb/Enums/EmailType.cs index 5abd2121..abc6fe8e 100644 --- a/Common/Models/EmailType.cs +++ b/Common/OpenShockDb/Enums/EmailType.cs @@ -1,26 +1,26 @@ using NpgsqlTypes; using OpenShock.Common.Utils; -namespace OpenShock.Common.Models; +namespace OpenShock.Common.OpenShockDb; /// -/// The kind of email an represents. The outbox stores +/// The kind of email an represents. The outbox stores /// only the intent to send one of these, never a rendered body or a usable secret, the /// consumer maps the type to the matching template and (for token-bearing types) mints a fresh /// secret at send time. /// -[PgEnum] +[PgEnum(Name = "email_type")] public enum EmailType { /// Account activation / email confirmation for a newly created account. - [PgName("account_activation")] AccountActivation, + [PgName("account_activation")] AccountActivation = 0, /// Password reset link. - [PgName("password_reset")] PasswordReset, + [PgName("password_reset")] PasswordReset = 1, /// Verification of a newly requested email address (sent to the new address). - [PgName("email_verification")] EmailVerification, + [PgName("email_verification")] EmailVerification = 2, /// Informational notice sent to the previous address when an email change is requested. Carries no secret. - [PgName("email_change_notice")] EmailChangeNotice + [PgName("email_change_notice")] EmailChangeNotice = 3 } diff --git a/Common/OpenShockDb/Enums/MatchTypeEnum.cs b/Common/OpenShockDb/Enums/MatchTypeEnum.cs new file mode 100644 index 00000000..06a0e31a --- /dev/null +++ b/Common/OpenShockDb/Enums/MatchTypeEnum.cs @@ -0,0 +1,11 @@ +using NpgsqlTypes; +using OpenShock.Common.Utils; + +namespace OpenShock.Common.OpenShockDb; + +[PgEnum(Name = "match_type_enum")] +public enum MatchTypeEnum +{ + [PgName("exact")] Exact = 0, + [PgName("contains")] Contains = 1, +} diff --git a/Common/OpenShockDb/Enums/OtaUpdateStatus.cs b/Common/OpenShockDb/Enums/OtaUpdateStatus.cs new file mode 100644 index 00000000..104c58b1 --- /dev/null +++ b/Common/OpenShockDb/Enums/OtaUpdateStatus.cs @@ -0,0 +1,14 @@ +using NpgsqlTypes; +using OpenShock.Common.Utils; + +namespace OpenShock.Common.OpenShockDb; + +[PgEnum(Name = "ota_update_status")] +public enum OtaUpdateStatus +{ + [PgName("started")] Started = 0, + [PgName("running")] Running = 1, + [PgName("finished")] Finished = 2, + [PgName("error")] Error = 3, + [PgName("timeout")] Timeout = 4 +} diff --git a/Common/Models/PasswordHashingAlgorithm.cs b/Common/OpenShockDb/Enums/PasswordHashingAlgorithm.cs similarity index 61% rename from Common/Models/PasswordHashingAlgorithm.cs rename to Common/OpenShockDb/Enums/PasswordHashingAlgorithm.cs index 9c3be23e..3198670a 100644 --- a/Common/Models/PasswordHashingAlgorithm.cs +++ b/Common/OpenShockDb/Enums/PasswordHashingAlgorithm.cs @@ -1,13 +1,14 @@ // ReSharper disable InconsistentNaming + using NpgsqlTypes; using OpenShock.Common.Utils; -namespace OpenShock.Common.Models; +namespace OpenShock.Common.OpenShockDb; -[PgEnum("password_encryption_type")] +[PgEnum(Name = "password_encryption_type")] public enum PasswordHashingAlgorithm { Unknown = -1, [PgName("bcrypt_enhanced")] BCrypt = 0, - [PgName("pbkdf2")] PBKDF2 = 1, -}; + [PgName("pbkdf2")] PBKDF2 = 1 +} diff --git a/Common/OpenShockDb/Enums/PermissionType.cs b/Common/OpenShockDb/Enums/PermissionType.cs new file mode 100644 index 00000000..7e86ca6c --- /dev/null +++ b/Common/OpenShockDb/Enums/PermissionType.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; +using NpgsqlTypes; +using OpenShock.Common.JsonSerialization; +using OpenShock.Common.Utils; + +// ReSharper disable InconsistentNaming + +namespace OpenShock.Common.OpenShockDb; + +[PgEnum(Name = "permission_type")] +[JsonConverter(typeof(PermissionTypeConverter))] +public enum PermissionType +{ + [PgName("shockers.use")] Shockers_Use = 0, + + [PgName("shockers.edit")] Shockers_Edit = 1, + + [PgName("shockers.pause")] Shockers_Pause = 2, + + [PgName("devices.edit")] Devices_Edit = 3, + + [PgName("devices.auth")] Devices_Auth = 4 +} diff --git a/Common/OpenShockDb/Enums/RoleType.cs b/Common/OpenShockDb/Enums/RoleType.cs new file mode 100644 index 00000000..ef68b4cb --- /dev/null +++ b/Common/OpenShockDb/Enums/RoleType.cs @@ -0,0 +1,13 @@ +using NpgsqlTypes; +using OpenShock.Common.Utils; + +namespace OpenShock.Common.OpenShockDb; + +[PgEnum(Name = "role_type")] +public enum RoleType +{ + [PgName("support")] Support = 0, + [PgName("staff")] Staff = 1, + [PgName("admin")] Admin = 2, + [PgName("system")] System = 3, +} diff --git a/Common/Models/ShockerModelType.cs b/Common/OpenShockDb/Enums/ShockerModelType.cs similarity index 58% rename from Common/Models/ShockerModelType.cs rename to Common/OpenShockDb/Enums/ShockerModelType.cs index 8247d439..1629c902 100644 --- a/Common/Models/ShockerModelType.cs +++ b/Common/OpenShockDb/Enums/ShockerModelType.cs @@ -1,13 +1,13 @@ -using NpgsqlTypes; +using NpgsqlTypes; using OpenShock.Common.Utils; -namespace OpenShock.Common.Models; +namespace OpenShock.Common.OpenShockDb; -[PgEnum] +[PgEnum(Name = "shocker_model_type")] public enum ShockerModelType { [PgName("cai_xianlin")] CaiXianlin = 0, [PgName("petrainer")] PetTrainer = 1, [PgName("petrainer_998dr")] Petrainer998DR = 2, - [PgName("wellturn_t330")] WellturnT330 = 3, + [PgName("wellturn_t330")] WellturnT330 = 3 } \ No newline at end of file diff --git a/Common/OpenShockDb/NpgsqlEnumExtensions.cs b/Common/OpenShockDb/Extensions/NpgsqlEnumExtensions.cs similarity index 82% rename from Common/OpenShockDb/NpgsqlEnumExtensions.cs rename to Common/OpenShockDb/Extensions/NpgsqlEnumExtensions.cs index 0b07fcde..98836b82 100644 --- a/Common/OpenShockDb/NpgsqlEnumExtensions.cs +++ b/Common/OpenShockDb/Extensions/NpgsqlEnumExtensions.cs @@ -1,21 +1,13 @@ using System.Reflection; -using System.Text.RegularExpressions; using Microsoft.EntityFrameworkCore; using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; using NpgsqlTypes; -using OpenShock.Common.Models; using OpenShock.Common.Utils; namespace OpenShock.Common.OpenShockDb; -public static partial class NpgsqlEnumExtensions +public static class NpgsqlEnumExtensions { - [GeneratedRegex("([a-z])([A-Z0-9])")] - private static partial Regex SnakeCaseRegex(); - - private static string ToSnakeCase(string name) => - SnakeCaseRegex().Replace(name, "$1_$2").ToLowerInvariant(); - private readonly record struct PgEnumInfo( Action Map, Action Register); @@ -26,7 +18,7 @@ private static PgEnumInfo BuildInfo() where TEnum : struct, Enum var attr = type.GetCustomAttribute() ?? throw new InvalidOperationException($"{type.Name} is missing [PgEnum]"); - var pgTypeName = attr.Name ?? ToSnakeCase(type.Name); + var pgTypeName = attr.Name; var members = type .GetFields(BindingFlags.Public | BindingFlags.Static) diff --git a/Common/OpenShockDb/EmailOutboxCoalesceKeys.cs b/Common/OpenShockDb/Keys/EmailOutboxCoalesceKeys.cs similarity index 93% rename from Common/OpenShockDb/EmailOutboxCoalesceKeys.cs rename to Common/OpenShockDb/Keys/EmailOutboxCoalesceKeys.cs index c7e5e31e..5986e959 100644 --- a/Common/OpenShockDb/EmailOutboxCoalesceKeys.cs +++ b/Common/OpenShockDb/Keys/EmailOutboxCoalesceKeys.cs @@ -11,7 +11,7 @@ namespace OpenShock.Common.OpenShockDb; /// The kind prefix is baked into the key so different email kinds for the same user never coalesce /// into each other (a pending reset must not suppress a pending activation). Scope is per user: only /// the newest activation / reset / verification for a given user is delivered. -/// intentionally has no builder - it is an +/// intentionally has no builder - it is an /// always-deliver type, so it carries a null key. /// public static class EmailOutboxCoalesceKeys diff --git a/Common/OpenShockDb/EmailOutboxPayloadKeys.cs b/Common/OpenShockDb/Keys/EmailOutboxPayloadKeys.cs similarity index 78% rename from Common/OpenShockDb/EmailOutboxPayloadKeys.cs rename to Common/OpenShockDb/Keys/EmailOutboxPayloadKeys.cs index 3e189664..ba9781e3 100644 --- a/Common/OpenShockDb/EmailOutboxPayloadKeys.cs +++ b/Common/OpenShockDb/Keys/EmailOutboxPayloadKeys.cs @@ -1,3 +1,4 @@ + namespace OpenShock.Common.OpenShockDb; /// @@ -9,15 +10,15 @@ namespace OpenShock.Common.OpenShockDb; /// public static class EmailOutboxPayloadKeys { - /// Target user id. Used by . + /// Target user id. Used by . public const string UserId = "userId"; - /// id. Used by . + /// id. Used by . public const string PasswordResetId = "passwordResetId"; - /// id. Used by . + /// id. Used by . public const string EmailChangeId = "emailChangeId"; - /// The newly requested address. Used by . + /// The newly requested address. Used by . public const string NewEmail = "newEmail"; } diff --git a/Common/OpenShockDb/AdminUsersView.cs b/Common/OpenShockDb/Models/AdminUsersView.cs similarity index 93% rename from Common/OpenShockDb/AdminUsersView.cs rename to Common/OpenShockDb/Models/AdminUsersView.cs index 536a57ec..0d7f64e8 100644 --- a/Common/OpenShockDb/AdminUsersView.cs +++ b/Common/OpenShockDb/Models/AdminUsersView.cs @@ -1,5 +1,4 @@ -using OpenShock.Common.Models; -// We are in a view, no need to restrict lengths lol +// We are in a view, no need to restrict lengths lol // ReSharper disable EntityFramework.ModelValidation.UnlimitedStringLength namespace OpenShock.Common.OpenShockDb; diff --git a/Common/OpenShockDb/ApiToken.cs b/Common/OpenShockDb/Models/ApiToken.cs similarity index 97% rename from Common/OpenShockDb/ApiToken.cs rename to Common/OpenShockDb/Models/ApiToken.cs index b1e3b139..343e957b 100644 --- a/Common/OpenShockDb/ApiToken.cs +++ b/Common/OpenShockDb/Models/ApiToken.cs @@ -1,6 +1,5 @@ using System.Net; using OpenShock.Common.Constants; -using OpenShock.Common.Models; namespace OpenShock.Common.OpenShockDb; diff --git a/Common/OpenShockDb/ApiTokenReport.cs b/Common/OpenShockDb/Models/ApiTokenReport.cs similarity index 100% rename from Common/OpenShockDb/ApiTokenReport.cs rename to Common/OpenShockDb/Models/ApiTokenReport.cs diff --git a/Common/OpenShockDb/ConfigurationItem.cs b/Common/OpenShockDb/Models/ConfigurationItem.cs similarity index 61% rename from Common/OpenShockDb/ConfigurationItem.cs rename to Common/OpenShockDb/Models/ConfigurationItem.cs index d0e58e0c..f9261705 100644 --- a/Common/OpenShockDb/ConfigurationItem.cs +++ b/Common/OpenShockDb/Models/ConfigurationItem.cs @@ -1,18 +1,5 @@ -using NpgsqlTypes; -using OpenShock.Common.Utils; - namespace OpenShock.Common.OpenShockDb; -[PgEnum] -public enum ConfigurationValueType -{ - [PgName("string")] String, - [PgName("bool")] Bool, - [PgName("int")] Int, - [PgName("float")] Float, - [PgName("json")] Json, -} - public sealed class ConfigurationItem { public required string Name { get; set; } diff --git a/Common/OpenShockDb/Device.cs b/Common/OpenShockDb/Models/Device.cs similarity index 100% rename from Common/OpenShockDb/Device.cs rename to Common/OpenShockDb/Models/Device.cs diff --git a/Common/OpenShockDb/DeviceOtaUpdate.cs b/Common/OpenShockDb/Models/DeviceOtaUpdate.cs similarity index 84% rename from Common/OpenShockDb/DeviceOtaUpdate.cs rename to Common/OpenShockDb/Models/DeviceOtaUpdate.cs index a2e4fea4..edab467b 100644 --- a/Common/OpenShockDb/DeviceOtaUpdate.cs +++ b/Common/OpenShockDb/Models/DeviceOtaUpdate.cs @@ -1,6 +1,4 @@ -using OpenShock.Common.Models; - -namespace OpenShock.Common.OpenShockDb; +namespace OpenShock.Common.OpenShockDb; public sealed class DeviceOtaUpdate { diff --git a/Common/OpenShockDb/DiscordWebhook.cs b/Common/OpenShockDb/Models/DiscordWebhook.cs similarity index 100% rename from Common/OpenShockDb/DiscordWebhook.cs rename to Common/OpenShockDb/Models/DiscordWebhook.cs diff --git a/Common/OpenShockDb/EmailOutboxMessage.cs b/Common/OpenShockDb/Models/EmailOutboxMessage.cs similarity index 99% rename from Common/OpenShockDb/EmailOutboxMessage.cs rename to Common/OpenShockDb/Models/EmailOutboxMessage.cs index 3b0b3342..53bc5f79 100644 --- a/Common/OpenShockDb/EmailOutboxMessage.cs +++ b/Common/OpenShockDb/Models/EmailOutboxMessage.cs @@ -1,5 +1,3 @@ -using OpenShock.Common.Models; - namespace OpenShock.Common.OpenShockDb; /// diff --git a/Common/OpenShockDb/EmailProviderBlacklist.cs b/Common/OpenShockDb/Models/EmailProviderBlacklist.cs similarity index 100% rename from Common/OpenShockDb/EmailProviderBlacklist.cs rename to Common/OpenShockDb/Models/EmailProviderBlacklist.cs diff --git a/Common/OpenShockDb/PublicShare.cs b/Common/OpenShockDb/Models/PublicShare.cs similarity index 100% rename from Common/OpenShockDb/PublicShare.cs rename to Common/OpenShockDb/Models/PublicShare.cs diff --git a/Common/OpenShockDb/PublicShareShocker.cs b/Common/OpenShockDb/Models/PublicShareShocker.cs similarity index 100% rename from Common/OpenShockDb/PublicShareShocker.cs rename to Common/OpenShockDb/Models/PublicShareShocker.cs diff --git a/Common/OpenShockDb/SafetySettings.cs b/Common/OpenShockDb/Models/SafetySettings.cs similarity index 100% rename from Common/OpenShockDb/SafetySettings.cs rename to Common/OpenShockDb/Models/SafetySettings.cs diff --git a/Common/OpenShockDb/Shocker.cs b/Common/OpenShockDb/Models/Shocker.cs similarity index 91% rename from Common/OpenShockDb/Shocker.cs rename to Common/OpenShockDb/Models/Shocker.cs index 89100c29..906ee9c2 100644 --- a/Common/OpenShockDb/Shocker.cs +++ b/Common/OpenShockDb/Models/Shocker.cs @@ -1,6 +1,4 @@ -using OpenShock.Common.Models; - -namespace OpenShock.Common.OpenShockDb; +namespace OpenShock.Common.OpenShockDb; public sealed class Shocker { diff --git a/Common/OpenShockDb/ShockerControlLog.cs b/Common/OpenShockDb/Models/ShockerControlLog.cs similarity index 89% rename from Common/OpenShockDb/ShockerControlLog.cs rename to Common/OpenShockDb/Models/ShockerControlLog.cs index d007f27d..2c2ab0f9 100644 --- a/Common/OpenShockDb/ShockerControlLog.cs +++ b/Common/OpenShockDb/Models/ShockerControlLog.cs @@ -1,6 +1,4 @@ -using OpenShock.Common.Models; - -namespace OpenShock.Common.OpenShockDb; +namespace OpenShock.Common.OpenShockDb; public sealed class ShockerControlLog { diff --git a/Common/OpenShockDb/ShockerShareCode.cs b/Common/OpenShockDb/Models/ShockerShareCode.cs similarity index 100% rename from Common/OpenShockDb/ShockerShareCode.cs rename to Common/OpenShockDb/Models/ShockerShareCode.cs diff --git a/Common/OpenShockDb/User.cs b/Common/OpenShockDb/Models/User.cs similarity index 96% rename from Common/OpenShockDb/User.cs rename to Common/OpenShockDb/Models/User.cs index bf5a2990..df26d85a 100644 --- a/Common/OpenShockDb/User.cs +++ b/Common/OpenShockDb/Models/User.cs @@ -1,6 +1,4 @@ -using OpenShock.Common.Models; - -namespace OpenShock.Common.OpenShockDb; +namespace OpenShock.Common.OpenShockDb; public sealed class User { diff --git a/Common/OpenShockDb/UserActivationRequest.cs b/Common/OpenShockDb/Models/UserActivationRequest.cs similarity index 100% rename from Common/OpenShockDb/UserActivationRequest.cs rename to Common/OpenShockDb/Models/UserActivationRequest.cs diff --git a/Common/OpenShockDb/UserDeactivation.cs b/Common/OpenShockDb/Models/UserDeactivation.cs similarity index 100% rename from Common/OpenShockDb/UserDeactivation.cs rename to Common/OpenShockDb/Models/UserDeactivation.cs diff --git a/Common/OpenShockDb/UserEmailChange.cs b/Common/OpenShockDb/Models/UserEmailChange.cs similarity index 100% rename from Common/OpenShockDb/UserEmailChange.cs rename to Common/OpenShockDb/Models/UserEmailChange.cs diff --git a/Common/OpenShockDb/UserNameBlacklist.cs b/Common/OpenShockDb/Models/UserNameBlacklist.cs similarity index 78% rename from Common/OpenShockDb/UserNameBlacklist.cs rename to Common/OpenShockDb/Models/UserNameBlacklist.cs index b24a37b7..7bce6b86 100644 --- a/Common/OpenShockDb/UserNameBlacklist.cs +++ b/Common/OpenShockDb/Models/UserNameBlacklist.cs @@ -1,15 +1,5 @@ -using NpgsqlTypes; -using OpenShock.Common.Utils; - namespace OpenShock.Common.OpenShockDb; -[PgEnum] -public enum MatchTypeEnum -{ - [PgName("exact")] Exact, - [PgName("contains")] Contains, -} - public sealed class UserNameBlacklist { public required Guid Id { get; set; } diff --git a/Common/OpenShockDb/UserNameChange.cs b/Common/OpenShockDb/Models/UserNameChange.cs similarity index 100% rename from Common/OpenShockDb/UserNameChange.cs rename to Common/OpenShockDb/Models/UserNameChange.cs diff --git a/Common/OpenShockDb/UserOAuthConnection.cs b/Common/OpenShockDb/Models/UserOAuthConnection.cs similarity index 100% rename from Common/OpenShockDb/UserOAuthConnection.cs rename to Common/OpenShockDb/Models/UserOAuthConnection.cs diff --git a/Common/OpenShockDb/UserPasswordReset.cs b/Common/OpenShockDb/Models/UserPasswordReset.cs similarity index 100% rename from Common/OpenShockDb/UserPasswordReset.cs rename to Common/OpenShockDb/Models/UserPasswordReset.cs diff --git a/Common/OpenShockDb/UserShare.cs b/Common/OpenShockDb/Models/UserShare.cs similarity index 100% rename from Common/OpenShockDb/UserShare.cs rename to Common/OpenShockDb/Models/UserShare.cs diff --git a/Common/OpenShockDb/UserShareInvite.cs b/Common/OpenShockDb/Models/UserShareInvite.cs similarity index 100% rename from Common/OpenShockDb/UserShareInvite.cs rename to Common/OpenShockDb/Models/UserShareInvite.cs diff --git a/Common/OpenShockDb/UserShareInviteShocker.cs b/Common/OpenShockDb/Models/UserShareInviteShocker.cs similarity index 100% rename from Common/OpenShockDb/UserShareInviteShocker.cs rename to Common/OpenShockDb/Models/UserShareInviteShocker.cs diff --git a/Common/OpenShockDb/EmailOutboxQueries.cs b/Common/OpenShockDb/Queries/EmailOutboxQueries.cs similarity index 98% rename from Common/OpenShockDb/EmailOutboxQueries.cs rename to Common/OpenShockDb/Queries/EmailOutboxQueries.cs index ade6d04a..f6239d8b 100644 --- a/Common/OpenShockDb/EmailOutboxQueries.cs +++ b/Common/OpenShockDb/Queries/EmailOutboxQueries.cs @@ -1,5 +1,4 @@ using Microsoft.EntityFrameworkCore; -using OpenShock.Common.Models; namespace OpenShock.Common.OpenShockDb; diff --git a/Common/Problems/CustomProblems/TokenPermissionProblem.cs b/Common/Problems/CustomProblems/TokenPermissionProblem.cs index d23b0598..203df2e7 100644 --- a/Common/Problems/CustomProblems/TokenPermissionProblem.cs +++ b/Common/Problems/CustomProblems/TokenPermissionProblem.cs @@ -1,5 +1,6 @@ using System.Net; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Problems.CustomProblems; diff --git a/Common/Redis/PubSub/DeviceMessage.cs b/Common/Redis/PubSub/DeviceMessage.cs index fb64859f..10635afd 100644 --- a/Common/Redis/PubSub/DeviceMessage.cs +++ b/Common/Redis/PubSub/DeviceMessage.cs @@ -1,5 +1,6 @@ using MessagePack; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Redis.PubSub; diff --git a/Common/Utils/HashingUtils.cs b/Common/Utils/HashingUtils.cs index c9574bcb..7634b250 100644 --- a/Common/Utils/HashingUtils.cs +++ b/Common/Utils/HashingUtils.cs @@ -4,6 +4,7 @@ using System.Text; using BCrypt.Net; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Utils; diff --git a/Common/Utils/PermissionUtils.cs b/Common/Utils/PermissionUtils.cs index 99307465..db3fd7ef 100644 --- a/Common/Utils/PermissionUtils.cs +++ b/Common/Utils/PermissionUtils.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Utils; diff --git a/Common/Utils/PgEnumAttribute.cs b/Common/Utils/PgEnumAttribute.cs index c72a30aa..05295976 100644 --- a/Common/Utils/PgEnumAttribute.cs +++ b/Common/Utils/PgEnumAttribute.cs @@ -4,13 +4,12 @@ namespace OpenShock.Common.Utils; /// Marks an enum as a Postgres native enum type so it is automatically registered /// with the EF Core model via . /// -/// -/// Explicit Postgres type name. When omitted, the C# type name is converted to snake_case. -/// -/// Postgres schema; defaults to the model's default schema when null. [AttributeUsage(AttributeTargets.Enum)] -public sealed class PgEnumAttribute(string? name = null, string? schema = null) : Attribute +public sealed class PgEnumAttribute : Attribute { - public string? Name { get; } = name; - public string? Schema { get; } = schema; + /// The Postgres type name. Always specified explicitly, e.g. [PgEnum(Name = "control_type")]. + public required string Name { get; init; } + + /// Postgres schema; defaults to the model's default schema when null. + public string? Schema { get; init; } } diff --git a/Cron/Services/Email/Outbox/EmailOutboxRetryPolicy.cs b/Cron/Services/Email/Outbox/EmailOutboxRetryPolicy.cs index b462c33e..29f84c8f 100644 --- a/Cron/Services/Email/Outbox/EmailOutboxRetryPolicy.cs +++ b/Cron/Services/Email/Outbox/EmailOutboxRetryPolicy.cs @@ -10,7 +10,7 @@ public static class EmailOutboxRetryPolicy { /// /// Maximum number of delivery attempts before a transiently-failing message is given up as - /// . An attempt is counted when the row is + /// . An attempt is counted when the row is /// claimed, so this also bounds crash/lease-expiry reclaim loops. Matches the prior Hangfire policy /// (one initial run plus ten retries). /// diff --git a/LiveControlGateway/Controllers/HubV1Controller.cs b/LiveControlGateway/Controllers/HubV1Controller.cs index 0d7f7115..eb161c22 100644 --- a/LiveControlGateway/Controllers/HubV1Controller.cs +++ b/LiveControlGateway/Controllers/HubV1Controller.cs @@ -5,6 +5,7 @@ using OpenShock.Common.Authentication; using OpenShock.Common.Hubs; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Services.Ota; using OpenShock.LiveControlGateway.LifetimeManager; using OpenShock.LiveControlGateway.Options; diff --git a/LiveControlGateway/Controllers/HubV2Controller.cs b/LiveControlGateway/Controllers/HubV2Controller.cs index e0600222..997ef655 100644 --- a/LiveControlGateway/Controllers/HubV2Controller.cs +++ b/LiveControlGateway/Controllers/HubV2Controller.cs @@ -13,6 +13,7 @@ using OpenShock.Serialization.Types; using Serilog; using System.Diagnostics; +using OpenShock.Common.OpenShockDb; namespace OpenShock.LiveControlGateway.Controllers; //TODO: Implement new keep alive ping pong mechanism diff --git a/LiveControlGateway/LifetimeManager/ShockerState.cs b/LiveControlGateway/LifetimeManager/ShockerState.cs index f8470dfc..03c8735b 100644 --- a/LiveControlGateway/LifetimeManager/ShockerState.cs +++ b/LiveControlGateway/LifetimeManager/ShockerState.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; namespace OpenShock.LiveControlGateway.LifetimeManager; diff --git a/LiveControlGateway/Mappers/FbsMapper.cs b/LiveControlGateway/Mappers/FbsMapper.cs index 11c54de6..0c85ab03 100644 --- a/LiveControlGateway/Mappers/FbsMapper.cs +++ b/LiveControlGateway/Mappers/FbsMapper.cs @@ -1,4 +1,5 @@ using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Redis.PubSub; using OpenShock.Serialization.Gateway; using OpenShock.Serialization.Types; @@ -7,14 +8,14 @@ namespace OpenShock.LiveControlGateway.Mappers; public static class FbsMapper { - public static Serialization.Types.ShockerModelType ToFbsModelType(Common.Models.ShockerModelType type) + public static Serialization.Types.ShockerModelType ToFbsModelType(Common.OpenShockDb.ShockerModelType type) { return type switch { - Common.Models.ShockerModelType.CaiXianlin => Serialization.Types.ShockerModelType.CaiXianlin, - Common.Models.ShockerModelType.PetTrainer => Serialization.Types.ShockerModelType.Petrainer, - Common.Models.ShockerModelType.Petrainer998DR => Serialization.Types.ShockerModelType.Petrainer998DR, - Common.Models.ShockerModelType.WellturnT330 => Serialization.Types.ShockerModelType.WellturnT330, + Common.OpenShockDb.ShockerModelType.CaiXianlin => Serialization.Types.ShockerModelType.CaiXianlin, + Common.OpenShockDb.ShockerModelType.PetTrainer => Serialization.Types.ShockerModelType.Petrainer, + Common.OpenShockDb.ShockerModelType.Petrainer998DR => Serialization.Types.ShockerModelType.Petrainer998DR, + Common.OpenShockDb.ShockerModelType.WellturnT330 => Serialization.Types.ShockerModelType.WellturnT330, _ => throw new NotImplementedException(), }; } From 204206a8776128ff391a64695ed09778b24a540b Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Jul 2026 15:39:40 +0200 Subject: [PATCH 4/5] refactor: drop redundant usings and qualifiers after the reorg Remove now-unnecessary OpenShock.Common.Models usings, unqualify Models.Response.* references, and tidy up import ordering left over from the OpenShockDb reorganization. --- API.IntegrationTests/Helpers/TestHelper.cs | 7 ++++--- API.IntegrationTests/Tests/TokensTests.cs | 3 ++- .../Account/Authenticated/ChangeUsername.cs | 7 +++---- API/Controller/Devices/DevicesController.cs | 19 ++++++++++--------- API/Controller/Sessions/DeleteSessions.cs | 7 +++---- API/Models/Requests/EditTokenRequest.cs | 1 - API/Models/Requests/EditTokenRequestV2.cs | 1 - API/Models/Requests/NewShocker.cs | 1 - API/Models/Response/ShockerResponse.cs | 3 +-- API/Models/Response/TokenCreatedResponse.cs | 3 +-- API/Models/Response/TokenCreatedResponseV2.cs | 2 -- API/Models/Response/TokenResponse.cs | 3 +-- API/Models/Response/TokenResponseV2.cs | 4 +--- Common.Tests/Utils/HashingUtilsTests.cs | 3 +-- .../ApiTokenPermissionRequirement.cs | 1 - Common/Errors/AuthorizationError.cs | 1 - .../Extensions/ClaimsPrincipalExtensions.cs | 1 - .../CustomProblems/TokenPermissionProblem.cs | 1 - Common/Utils/HashingUtils.cs | 4 +--- .../Controllers/HubV1Controller.cs | 8 +++++--- .../Controllers/HubV2Controller.cs | 6 +++--- .../LifetimeManager/ShockerState.cs | 1 - LiveControlGateway/Mappers/FbsMapper.cs | 14 +++++++------- 23 files changed, 43 insertions(+), 58 deletions(-) diff --git a/API.IntegrationTests/Helpers/TestHelper.cs b/API.IntegrationTests/Helpers/TestHelper.cs index f71f5608..49b53e35 100644 --- a/API.IntegrationTests/Helpers/TestHelper.cs +++ b/API.IntegrationTests/Helpers/TestHelper.cs @@ -2,6 +2,7 @@ using System.Net; using System.Text; using System.Text.Json; +using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using OpenShock.Common.Constants; using OpenShock.Common.OpenShockDb; @@ -50,7 +51,7 @@ public static async Task CreateAndLoginUser( /// public static HttpClient CreateAuthenticatedClient(WebApplicationFactory factory, string sessionToken) { - var client = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions + var client = factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false, HandleCookies = false @@ -64,7 +65,7 @@ public static HttpClient CreateAuthenticatedClient(WebApplicationFactory factory /// public static HttpClient CreateApiTokenClient(WebApplicationFactory factory, string apiToken) { - var client = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions + var client = factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false, HandleCookies = false @@ -78,7 +79,7 @@ public static HttpClient CreateApiTokenClient(WebApplicationFactory factory, str /// public static HttpClient CreateHubTokenClient(WebApplicationFactory factory, string hubToken) { - var client = factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions + var client = factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false, HandleCookies = false diff --git a/API.IntegrationTests/Tests/TokensTests.cs b/API.IntegrationTests/Tests/TokensTests.cs index ca022254..b6dd3b8e 100644 --- a/API.IntegrationTests/Tests/TokensTests.cs +++ b/API.IntegrationTests/Tests/TokensTests.cs @@ -1,6 +1,7 @@ using System.Net; using System.Text.Json; using OpenShock.API.IntegrationTests.Helpers; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.IntegrationTests.Tests; @@ -348,7 +349,7 @@ public async Task ApiTokenAuth_CanAccessDevices() { var userId = await TestHelper.CreateUserInDb(WebApplicationFactory, "tokauth", "tokauth@test.org", "SecurePassword123#"); var (_, rawToken) = await TestHelper.CreateApiTokenInDb(WebApplicationFactory, userId, "AuthToken", - [Common.OpenShockDb.PermissionType.Shockers_Use, Common.OpenShockDb.PermissionType.Devices_Edit]); + [PermissionType.Shockers_Use, PermissionType.Devices_Edit]); using var client = TestHelper.CreateApiTokenClient(WebApplicationFactory, rawToken); var response = await client.GetAsync("/1/devices"); diff --git a/API/Controller/Account/Authenticated/ChangeUsername.cs b/API/Controller/Account/Authenticated/ChangeUsername.cs index 28b343ec..e346d20f 100644 --- a/API/Controller/Account/Authenticated/ChangeUsername.cs +++ b/API/Controller/Account/Authenticated/ChangeUsername.cs @@ -1,10 +1,9 @@ -using Microsoft.AspNetCore.Mvc; +using System.Net.Mime; +using Microsoft.AspNetCore.Mvc; using OpenShock.API.Models.Requests; using OpenShock.Common.Errors; -using OpenShock.Common.Models; -using OpenShock.Common.Problems; -using System.Net.Mime; using OpenShock.Common.OpenShockDb; +using OpenShock.Common.Problems; namespace OpenShock.API.Controller.Account.Authenticated; diff --git a/API/Controller/Devices/DevicesController.cs b/API/Controller/Devices/DevicesController.cs index 335734b5..46539308 100644 --- a/API/Controller/Devices/DevicesController.cs +++ b/API/Controller/Devices/DevicesController.cs @@ -1,19 +1,20 @@ -using Asp.Versioning; +using System.Net.Mime; +using System.Security.Cryptography; +using Asp.Versioning; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using OpenShock.API.Models.Requests; +using OpenShock.API.Models.Response; +using OpenShock.API.Services.DeviceUpdate; using OpenShock.Common.Authentication.Attributes; using OpenShock.Common.Constants; using OpenShock.Common.Errors; using OpenShock.Common.Extensions; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Problems; using OpenShock.Common.Redis; using OpenShock.Common.Utils; -using System.Net.Mime; -using System.Security.Cryptography; -using OpenShock.API.Services.DeviceUpdate; -using OpenShock.Common.OpenShockDb; using Redis.OM; namespace OpenShock.API.Controller.Devices; @@ -26,12 +27,12 @@ public sealed partial class DevicesController /// All devices for the current user [HttpGet] [MapToApiVersion("1")] - [ProducesResponseType>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] + [ProducesResponseType>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] public IActionResult ListDevices() { var devices = _db.Devices .Where(x => x.OwnerId == CurrentUser.Id) - .Select(x => new Models.Response.DeviceResponse + .Select(x => new DeviceResponse { Id = x.Id, Name = x.Name, @@ -48,7 +49,7 @@ public IActionResult ListDevices() /// /// The device [HttpGet("{deviceId}")] - [ProducesResponseType>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] + [ProducesResponseType>(StatusCodes.Status200OK, MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status404NotFound, MediaTypeNames.Application.ProblemJson)] // DeviceNotFound [MapToApiVersion("1")] public async Task GetDeviceById([FromRoute] Guid deviceId) @@ -57,7 +58,7 @@ public async Task GetDeviceById([FromRoute] Guid deviceId) var device = await _db.Devices.Where(x => x.OwnerId == CurrentUser.Id && x.Id == deviceId) - .Select(x => new Models.Response.DeviceWithTokenResponse + .Select(x => new DeviceWithTokenResponse { Id = x.Id, Name = x.Name, diff --git a/API/Controller/Sessions/DeleteSessions.cs b/API/Controller/Sessions/DeleteSessions.cs index b84f0e43..b950141f 100644 --- a/API/Controller/Sessions/DeleteSessions.cs +++ b/API/Controller/Sessions/DeleteSessions.cs @@ -1,10 +1,9 @@ -using Microsoft.AspNetCore.Mvc; +using System.Net.Mime; +using Microsoft.AspNetCore.Mvc; using OpenShock.Common.Errors; using OpenShock.Common.Extensions; -using OpenShock.Common.Models; -using OpenShock.Common.Problems; -using System.Net.Mime; using OpenShock.Common.OpenShockDb; +using OpenShock.Common.Problems; namespace OpenShock.API.Controller.Sessions; diff --git a/API/Models/Requests/EditTokenRequest.cs b/API/Models/Requests/EditTokenRequest.cs index afd12afc..cebfdfb4 100644 --- a/API/Models/Requests/EditTokenRequest.cs +++ b/API/Models/Requests/EditTokenRequest.cs @@ -1,6 +1,5 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Requests; diff --git a/API/Models/Requests/EditTokenRequestV2.cs b/API/Models/Requests/EditTokenRequestV2.cs index 0f738009..c9276bb1 100644 --- a/API/Models/Requests/EditTokenRequestV2.cs +++ b/API/Models/Requests/EditTokenRequestV2.cs @@ -1,6 +1,5 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Requests; diff --git a/API/Models/Requests/NewShocker.cs b/API/Models/Requests/NewShocker.cs index 7c4c7658..01ea346e 100644 --- a/API/Models/Requests/NewShocker.cs +++ b/API/Models/Requests/NewShocker.cs @@ -1,6 +1,5 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Requests; diff --git a/API/Models/Response/ShockerResponse.cs b/API/Models/Response/ShockerResponse.cs index 2df2d52f..1bb68783 100644 --- a/API/Models/Response/ShockerResponse.cs +++ b/API/Models/Response/ShockerResponse.cs @@ -1,5 +1,4 @@ -using OpenShock.Common.Models; -using OpenShock.Common.OpenShockDb; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/TokenCreatedResponse.cs b/API/Models/Response/TokenCreatedResponse.cs index 016b0bb8..27564902 100644 --- a/API/Models/Response/TokenCreatedResponse.cs +++ b/API/Models/Response/TokenCreatedResponse.cs @@ -1,5 +1,4 @@ -using OpenShock.Common.Models; -using OpenShock.Common.OpenShockDb; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/TokenCreatedResponseV2.cs b/API/Models/Response/TokenCreatedResponseV2.cs index 04b34ef4..286b774f 100644 --- a/API/Models/Response/TokenCreatedResponseV2.cs +++ b/API/Models/Response/TokenCreatedResponseV2.cs @@ -1,5 +1,3 @@ -using OpenShock.API.Models; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/TokenResponse.cs b/API/Models/Response/TokenResponse.cs index eb9ef60e..ac457104 100644 --- a/API/Models/Response/TokenResponse.cs +++ b/API/Models/Response/TokenResponse.cs @@ -1,5 +1,4 @@ -using OpenShock.Common.Models; -using OpenShock.Common.OpenShockDb; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/API/Models/Response/TokenResponseV2.cs b/API/Models/Response/TokenResponseV2.cs index cf7829ba..88c1f743 100644 --- a/API/Models/Response/TokenResponseV2.cs +++ b/API/Models/Response/TokenResponseV2.cs @@ -1,6 +1,4 @@ -using OpenShock.API.Models; -using OpenShock.Common.Models; -using OpenShock.Common.OpenShockDb; +using OpenShock.Common.OpenShockDb; namespace OpenShock.API.Models.Response; diff --git a/Common.Tests/Utils/HashingUtilsTests.cs b/Common.Tests/Utils/HashingUtilsTests.cs index fbb3b760..099b0dec 100644 --- a/Common.Tests/Utils/HashingUtilsTests.cs +++ b/Common.Tests/Utils/HashingUtilsTests.cs @@ -1,5 +1,4 @@ -using OpenShock.Common.Models; -using OpenShock.Common.OpenShockDb; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Utils; namespace OpenShock.Common.Tests.Utils; diff --git a/Common/Authentication/Requirements/ApiTokenPermissionRequirement.cs b/Common/Authentication/Requirements/ApiTokenPermissionRequirement.cs index ef008e81..a5c372aa 100644 --- a/Common/Authentication/Requirements/ApiTokenPermissionRequirement.cs +++ b/Common/Authentication/Requirements/ApiTokenPermissionRequirement.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Authorization; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Authentication.Requirements; diff --git a/Common/Errors/AuthorizationError.cs b/Common/Errors/AuthorizationError.cs index d78e868b..7753103b 100644 --- a/Common/Errors/AuthorizationError.cs +++ b/Common/Errors/AuthorizationError.cs @@ -1,5 +1,4 @@ using System.Net; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; using OpenShock.Common.Problems; using OpenShock.Common.Problems.CustomProblems; diff --git a/Common/Extensions/ClaimsPrincipalExtensions.cs b/Common/Extensions/ClaimsPrincipalExtensions.cs index 115d0bd9..d0e7fbcc 100644 --- a/Common/Extensions/ClaimsPrincipalExtensions.cs +++ b/Common/Extensions/ClaimsPrincipalExtensions.cs @@ -1,6 +1,5 @@ using System.Security.Claims; using OpenShock.Common.Authentication; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Extensions; diff --git a/Common/Problems/CustomProblems/TokenPermissionProblem.cs b/Common/Problems/CustomProblems/TokenPermissionProblem.cs index 203df2e7..334179bb 100644 --- a/Common/Problems/CustomProblems/TokenPermissionProblem.cs +++ b/Common/Problems/CustomProblems/TokenPermissionProblem.cs @@ -1,5 +1,4 @@ using System.Net; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Problems.CustomProblems; diff --git a/Common/Utils/HashingUtils.cs b/Common/Utils/HashingUtils.cs index 7634b250..b375b538 100644 --- a/Common/Utils/HashingUtils.cs +++ b/Common/Utils/HashingUtils.cs @@ -1,9 +1,7 @@ -using System.Buffers; -using System.Diagnostics; +using System.Diagnostics; using System.Security.Cryptography; using System.Text; using BCrypt.Net; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; namespace OpenShock.Common.Utils; diff --git a/LiveControlGateway/Controllers/HubV1Controller.cs b/LiveControlGateway/Controllers/HubV1Controller.cs index eb161c22..9d3a067f 100644 --- a/LiveControlGateway/Controllers/HubV1Controller.cs +++ b/LiveControlGateway/Controllers/HubV1Controller.cs @@ -12,6 +12,8 @@ using OpenShock.Serialization.Deprecated.DoNotUse.V1; using OpenShock.Serialization.Types; using Serilog; +using ShockerCommand = OpenShock.Serialization.Gateway.ShockerCommand; +using ShockerModelType = OpenShock.Serialization.Types.ShockerModelType; namespace OpenShock.LiveControlGateway.Controllers; @@ -164,16 +166,16 @@ await otaService.Error(CurrentHubId, payload.BootStatus.OtaUpdateId, false, /// [NonAction] - public override ValueTask Control(IList controlCommands) + public override ValueTask Control(IList controlCommands) => QueueMessage(new GatewayToHubMessage { Payload = new GatewayToHubMessagePayload(new ShockerCommandList { - Commands = [.. controlCommands.Select(x => new ShockerCommand() + Commands = [.. controlCommands.Select(x => new Serialization.Deprecated.DoNotUse.V1.ShockerCommand() { Duration = x.Duration, Type = x.Type, - Id = x.Model == Serialization.Types.ShockerModelType.Petrainer998DR ? (ushort)(x.Id >> 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 + Id = x.Model == ShockerModelType.Petrainer998DR ? (ushort)(x.Id >> 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 })] diff --git a/LiveControlGateway/Controllers/HubV2Controller.cs b/LiveControlGateway/Controllers/HubV2Controller.cs index 997ef655..a7dc7d4d 100644 --- a/LiveControlGateway/Controllers/HubV2Controller.cs +++ b/LiveControlGateway/Controllers/HubV2Controller.cs @@ -1,4 +1,5 @@ -using Asp.Versioning; +using System.Diagnostics; +using Asp.Versioning; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; @@ -6,14 +7,13 @@ using OpenShock.Common.Constants; using OpenShock.Common.Hubs; using OpenShock.Common.Models; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Services.Ota; using OpenShock.LiveControlGateway.LifetimeManager; using OpenShock.LiveControlGateway.Options; using OpenShock.Serialization.Gateway; using OpenShock.Serialization.Types; using Serilog; -using System.Diagnostics; -using OpenShock.Common.OpenShockDb; namespace OpenShock.LiveControlGateway.Controllers; //TODO: Implement new keep alive ping pong mechanism diff --git a/LiveControlGateway/LifetimeManager/ShockerState.cs b/LiveControlGateway/LifetimeManager/ShockerState.cs index 03c8735b..fa174de5 100644 --- a/LiveControlGateway/LifetimeManager/ShockerState.cs +++ b/LiveControlGateway/LifetimeManager/ShockerState.cs @@ -1,6 +1,5 @@ using System.ComponentModel.DataAnnotations; using OpenShock.Common.Constants; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; namespace OpenShock.LiveControlGateway.LifetimeManager; diff --git a/LiveControlGateway/Mappers/FbsMapper.cs b/LiveControlGateway/Mappers/FbsMapper.cs index 0c85ab03..a751c196 100644 --- a/LiveControlGateway/Mappers/FbsMapper.cs +++ b/LiveControlGateway/Mappers/FbsMapper.cs @@ -1,21 +1,21 @@ -using OpenShock.Common.Models; -using OpenShock.Common.OpenShockDb; +using OpenShock.Common.OpenShockDb; using OpenShock.Common.Redis.PubSub; using OpenShock.Serialization.Gateway; using OpenShock.Serialization.Types; +using ShockerModelType = OpenShock.Serialization.Types.ShockerModelType; namespace OpenShock.LiveControlGateway.Mappers; public static class FbsMapper { - public static Serialization.Types.ShockerModelType ToFbsModelType(Common.OpenShockDb.ShockerModelType type) + public static ShockerModelType ToFbsModelType(Common.OpenShockDb.ShockerModelType type) { return type switch { - Common.OpenShockDb.ShockerModelType.CaiXianlin => Serialization.Types.ShockerModelType.CaiXianlin, - Common.OpenShockDb.ShockerModelType.PetTrainer => Serialization.Types.ShockerModelType.Petrainer, - Common.OpenShockDb.ShockerModelType.Petrainer998DR => Serialization.Types.ShockerModelType.Petrainer998DR, - Common.OpenShockDb.ShockerModelType.WellturnT330 => Serialization.Types.ShockerModelType.WellturnT330, + Common.OpenShockDb.ShockerModelType.CaiXianlin => ShockerModelType.CaiXianlin, + Common.OpenShockDb.ShockerModelType.PetTrainer => ShockerModelType.Petrainer, + Common.OpenShockDb.ShockerModelType.Petrainer998DR => ShockerModelType.Petrainer998DR, + Common.OpenShockDb.ShockerModelType.WellturnT330 => ShockerModelType.WellturnT330, _ => throw new NotImplementedException(), }; } From bc6f1bfa29beb366dd2396f3bda9acb2f4f4dae9 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Jul 2026 15:39:40 +0200 Subject: [PATCH 5/5] fix(pg-enums): honor PgEnum schema in MapEnum Pass attr.Schema to MapEnum so schema-qualified Postgres enums are mapped in the correct schema instead of the default one, and fully-qualify the cref in PgEnumAttribute. Refresh OpenShockContextModelSnapshot after the enum/namespace changes. --- Common/Migrations/OpenShockContextModelSnapshot.cs | 1 - Common/OpenShockDb/Extensions/NpgsqlEnumExtensions.cs | 2 +- Common/Utils/PgEnumAttribute.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Common/Migrations/OpenShockContextModelSnapshot.cs b/Common/Migrations/OpenShockContextModelSnapshot.cs index 65c2be7a..da71049c 100644 --- a/Common/Migrations/OpenShockContextModelSnapshot.cs +++ b/Common/Migrations/OpenShockContextModelSnapshot.cs @@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using OpenShock.Common.Models; using OpenShock.Common.OpenShockDb; #nullable disable diff --git a/Common/OpenShockDb/Extensions/NpgsqlEnumExtensions.cs b/Common/OpenShockDb/Extensions/NpgsqlEnumExtensions.cs index 98836b82..583d8b1b 100644 --- a/Common/OpenShockDb/Extensions/NpgsqlEnumExtensions.cs +++ b/Common/OpenShockDb/Extensions/NpgsqlEnumExtensions.cs @@ -28,7 +28,7 @@ private static PgEnumInfo BuildInfo() where TEnum : struct, Enum .ToArray(); return new PgEnumInfo( - Map: b => b.MapEnum(pgTypeName), + Map: b => b.MapEnum(pgTypeName, attr.Schema), Register: m => m.HasPostgresEnum(attr.Schema, pgTypeName, members)); } diff --git a/Common/Utils/PgEnumAttribute.cs b/Common/Utils/PgEnumAttribute.cs index 05295976..76ce7341 100644 --- a/Common/Utils/PgEnumAttribute.cs +++ b/Common/Utils/PgEnumAttribute.cs @@ -2,7 +2,7 @@ namespace OpenShock.Common.Utils; /// /// Marks an enum as a Postgres native enum type so it is automatically registered -/// with the EF Core model via . +/// with the EF Core model via . /// [AttributeUsage(AttributeTargets.Enum)] public sealed class PgEnumAttribute : Attribute