feat: device reset via RST token + Postgres code-first migrations

Device reset: Device.ResetRequested is a one-shot flag claimed atomically with a single UPDATE, so a queued reset is delivered exactly once. GET api/telemetry/AddData answers it with the bare token RST and nothing else; subsequent requests resume the normal routine. Adds POST api/admin/devices/{id}/request-reset (audited) plus an admin panel button, pending badge and confirm dialog.

Postgres migrations: PostgresGreenHomeDbContext owns Data/Migrations/Postgres while the SqlServer set stays untouched. Registers one context per provider with the base type forwarded, replaces EnsureCreated() with Migrate() for both, adds a complete InitialPostgres migration and an incremental AddDeviceResetRequested for SqlServer, and adds design-time factories so dotnet ef does not have to boot the API host.

Timestamps: Postgres DateTime columns are now timestamp with time zone, because Npgsql rejects Kind=Utc values on a naive timestamp column, which broke every write path (auth, settings, posts, checklists, alerts, audit logs). Removes the SpecifyKind(Unspecified) workarounds, fixes a 3:30 offset where already-UTC filters were passed through ToLocalTime(), and makes DataCopy migrate instead of EnsureCreated while passing timestamps through verbatim.

Tests: Test/GreenHome.SelfCheck applies the Postgres migrations and asserts that a queued reset is consumed exactly once and that timestamps round-trip as Utc.
This commit is contained in:
2026-10-17 16:20:52 +03:30
parent d2dd91929a
commit e98b48bfba
33 changed files with 7130 additions and 474 deletions

View File

@@ -107,6 +107,9 @@ public sealed class AdminDeviceDetailDto
public bool IsOnline { get; set; }
public int MemberCount { get; set; }
/// <summary>درخواست ریست ثبت شده و هنوز مصرف نشده است (RST در آپلود بعدی ارسال می‌شود)</summary>
public bool ResetRequested { get; set; }
public string? ProductType { get; set; }
public string? Province { get; set; }
public string? City { get; set; }