fix(seeder): patch existing admin username/password on every boot
CI/CD / CI · API (dotnet build + test) (push) Has been cancelled
CI/CD / CI · Admin API (dotnet build) (push) Has been cancelled
CI/CD / CI · Dashboard (tsc) (push) Has been cancelled
CI/CD / CI · Admin Web (tsc) (push) Has been cancelled
CI/CD / CI · Website (tsc) (push) Has been cancelled
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
CI/CD / CI · API (dotnet build + test) (push) Has been cancelled
CI/CD / CI · Admin API (dotnet build) (push) Has been cancelled
CI/CD / CI · Dashboard (tsc) (push) Has been cancelled
CI/CD / CI · Admin Web (tsc) (push) Has been cancelled
CI/CD / CI · Website (tsc) (push) Has been cancelled
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
EnsureOwnerAdminAsync now sets Username='admin' (configurable via Seed:SystemAdminUsername) on any existing admin that has no username, and hashes Seed:SystemAdminPassword if provided and no hash is stored. Covers fresh deploys and existing prod admins created before credentials were added. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -54,9 +54,11 @@ public static class PlatformDataSeeder
|
|||||||
private static async Task EnsureOwnerAdminAsync(AppDbContext db, IConfiguration config, ILogger logger)
|
private static async Task EnsureOwnerAdminAsync(AppDbContext db, IConfiguration config, ILogger logger)
|
||||||
{
|
{
|
||||||
const string DefaultOwnerPhone = "09190345606";
|
const string DefaultOwnerPhone = "09190345606";
|
||||||
var configured = config["Seed:SystemAdminPhone"];
|
const string DefaultAdminUsername = "admin";
|
||||||
|
|
||||||
|
var configuredPhone = config["Seed:SystemAdminPhone"];
|
||||||
var phone = PhoneNormalizer.Normalize(
|
var phone = PhoneNormalizer.Normalize(
|
||||||
string.IsNullOrWhiteSpace(configured) ? DefaultOwnerPhone : configured);
|
string.IsNullOrWhiteSpace(configuredPhone) ? DefaultOwnerPhone : configuredPhone);
|
||||||
|
|
||||||
if (!PhoneNormalizer.IsValidIranMobile(phone))
|
if (!PhoneNormalizer.IsValidIranMobile(phone))
|
||||||
{
|
{
|
||||||
@@ -64,27 +66,53 @@ public static class PlatformDataSeeder
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await db.SystemAdmins.AnyAsync(a => a.Phone == phone))
|
var configuredUsername = config["Seed:SystemAdminUsername"];
|
||||||
return;
|
var username = string.IsNullOrWhiteSpace(configuredUsername) ? DefaultAdminUsername : configuredUsername.Trim().ToLowerInvariant();
|
||||||
|
var defaultPassword = config["Seed:SystemAdminPassword"]; // optional — only set if provided
|
||||||
|
|
||||||
db.SystemAdmins.Add(new SystemAdmin
|
var existing = await db.SystemAdmins.FirstOrDefaultAsync(a => a.Phone == phone);
|
||||||
|
|
||||||
|
if (existing is null)
|
||||||
{
|
{
|
||||||
Id = "sysadmin_owner",
|
var admin = new SystemAdmin
|
||||||
Name = "مدیر سامانه",
|
{
|
||||||
Phone = phone,
|
Id = "sysadmin_owner",
|
||||||
IsActive = true
|
Name = "مدیر سامانه",
|
||||||
});
|
Phone = phone,
|
||||||
|
IsActive = true,
|
||||||
|
Username = username,
|
||||||
|
PasswordHash = string.IsNullOrWhiteSpace(defaultPassword) ? null : PasswordHasher.Hash(defaultPassword)
|
||||||
|
};
|
||||||
|
db.SystemAdmins.Add(admin);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
logger.LogInformation("Seeded owner system admin with phone {Phone}, username '{Username}'", phone, username);
|
||||||
|
}
|
||||||
|
catch (DbUpdateException)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Owner system admin already seeded by another instance");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch existing admin: fill in missing username / password without overwriting set values
|
||||||
|
var patched = false;
|
||||||
|
if (string.IsNullOrWhiteSpace(existing.Username))
|
||||||
|
{
|
||||||
|
existing.Username = username;
|
||||||
|
patched = true;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(existing.PasswordHash) && !string.IsNullOrWhiteSpace(defaultPassword))
|
||||||
|
{
|
||||||
|
existing.PasswordHash = PasswordHasher.Hash(defaultPassword);
|
||||||
|
patched = true;
|
||||||
|
}
|
||||||
|
if (patched)
|
||||||
{
|
{
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
logger.LogInformation("Seeded owner system admin with phone {Phone}", phone);
|
logger.LogInformation("Patched owner system admin credentials (username/password)");
|
||||||
}
|
|
||||||
catch (DbUpdateException)
|
|
||||||
{
|
|
||||||
// api + admin-api boot concurrently against the same DB; another instance
|
|
||||||
// already inserted this admin. Safe to ignore.
|
|
||||||
logger.LogInformation("Owner system admin already seeded by another instance");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user