Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,599 changes: 1,599 additions & 0 deletions Common/Migrations/20260630233834_RefactorPgEnums.Designer.cs

Large diffs are not rendered by default.

77 changes: 77 additions & 0 deletions Common/Migrations/20260630233834_RefactorPgEnums.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace OpenShock.Common.Migrations
{
/// <inheritdoc />
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.

/// <inheritdoc />
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;");
}

/// <inheritdoc />
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;");
}
}
}
6 changes: 3 additions & 3 deletions Common/Migrations/OpenShockContextModelSnapshot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ 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, "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[] { "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[] { "cai_xianlin", "petrainer", "petrainer_998dr", "wellturn_t330" });
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);

modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
Expand Down
8 changes: 6 additions & 2 deletions Common/Models/ControlLimitMode.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
using NpgsqlTypes;
using OpenShock.Common.Utils;

namespace OpenShock.Common.Models;

/// <summary>
/// Determines how a per-token min/max limit is applied to an incoming control value.
/// </summary>
[PgEnum]
public enum ControlLimitMode
{
/// <summary>
/// Clamp the incoming value into the [min, max] range.
/// </summary>
Clamp = 0,
[PgName("clamp")] Clamp = 0,

/// <summary>
/// Linearly remap the full input range onto [min, max].
/// </summary>
Lerp = 1
[PgName("lerp")] Lerp = 1,
}
16 changes: 10 additions & 6 deletions Common/Models/ControlType.cs
Original file line number Diff line number Diff line change
@@ -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
}
[PgName("stop")] Stop = 0,
[PgName("shock")] Shock = 1,
[PgName("vibrate")] Vibrate = 2,
[PgName("sound")] Sound = 3,
}
14 changes: 9 additions & 5 deletions Common/Models/EmailStatus.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using NpgsqlTypes;
using OpenShock.Common.Utils;

namespace OpenShock.Common.Models;

/// <summary>
Expand All @@ -13,6 +16,7 @@ namespace OpenShock.Common.Models;
/// <see cref="OpenShockDb.EmailOutboxMessage.NextAttemptAt"/>), so the table is the complete, queryable
/// source of truth for every email's delivery state.
/// </remarks>
[PgEnum]
public enum EmailStatus
{
/// <summary>
Expand All @@ -21,31 +25,31 @@ public enum EmailStatus
/// future and <see cref="OpenShockDb.EmailOutboxMessage.AttemptCount"/> &gt; 0). A row is eligible
/// to be claimed once its next-attempt time has passed.
/// </summary>
Pending,
[PgName("pending")] Pending,

/// <summary>
/// Claimed by an executor and currently being delivered. The claim carries a lease
/// (<see cref="OpenShockDb.EmailOutboxMessage.NextAttemptAt"/> 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.
/// </summary>
Sending,
[PgName("sending")] Sending,

/// <summary>The message was handed to the email provider successfully. Terminal.</summary>
Sent,
[PgName("sent")] Sent,

/// <summary>
/// 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.
/// </summary>
Failed,
[PgName("failed")] Failed,

/// <summary>
/// The email was intentionally not sent because the underlying request no longer needs it (the
/// request was used, expired, superseded by a newer credential change, or no longer exists). This
/// is a successful no-op, kept distinct from <see cref="Failed"/> so operators never mistake it for
/// a delivery problem or requeue it. Terminal.
/// </summary>
Skipped
[PgName("skipped")] Skipped
}
12 changes: 8 additions & 4 deletions Common/Models/EmailType.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using NpgsqlTypes;
using OpenShock.Common.Utils;

namespace OpenShock.Common.Models;

/// <summary>
Expand All @@ -6,17 +9,18 @@ namespace OpenShock.Common.Models;
/// consumer maps the type to the matching template and (for token-bearing types) mints a fresh
/// secret at send time.
/// </summary>
[PgEnum]
public enum EmailType
{
/// <summary>Account activation / email confirmation for a newly created account.</summary>
AccountActivation,
[PgName("account_activation")] AccountActivation,

/// <summary>Password reset link.</summary>
PasswordReset,
[PgName("password_reset")] PasswordReset,

/// <summary>Verification of a newly requested email address (sent to the new address).</summary>
EmailVerification,
[PgName("email_verification")] EmailVerification,

/// <summary>Informational notice sent to the previous address when an email change is requested. Carries no secret.</summary>
EmailChangeNotice
[PgName("email_change_notice")] EmailChangeNotice
}
18 changes: 11 additions & 7 deletions Common/Models/OtaUpdateStatus.cs
Original file line number Diff line number Diff line change
@@ -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
}
[PgName("started")] Started,
[PgName("running")] Running,
[PgName("finished")] Finished,
[PgName("error")] Error,
[PgName("timeout")] Timeout,
}
12 changes: 8 additions & 4 deletions Common/Models/PasswordHashingAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -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,
};
[PgName("bcrypt_enhanced")] BCrypt = 0,
[PgName("pbkdf2")] PBKDF2 = 1,
};
2 changes: 2 additions & 0 deletions Common/Models/PermissionType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
16 changes: 10 additions & 6 deletions Common/Models/RoleType.cs
Original file line number Diff line number Diff line change
@@ -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
}
[PgName("support")] Support,
[PgName("staff")] Staff,
[PgName("admin")] Admin,
[PgName("system")] System,
}
10 changes: 6 additions & 4 deletions Common/Models/ShockerModelType.cs
Original file line number Diff line number Diff line change
@@ -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("petrainer998DR")] Petrainer998DR = 2,
[PgName("wellturnT330")] WellturnT330 = 3,
[PgName("cai_xianlin")] CaiXianlin = 0,
[PgName("petrainer")] PetTrainer = 1,
[PgName("petrainer_998dr")] Petrainer998DR = 2,
[PgName("wellturn_t330")] WellturnT330 = 3,
}
16 changes: 10 additions & 6 deletions Common/OpenShockDb/ConfigurationItem.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading
Loading