Files
voltsrage 2a3ef62a7d
CI / frontend (push) Failing after 57s
CI / backend (push) Failing after 6m27s
Add deployment
2026-08-05 00:26:20 +08:00

82 lines
2.7 KiB
C#

using Microsoft.EntityFrameworkCore;
/// <summary>
/// One-shot bootstrap: creates the first Admin user when the ClinicalUsers
/// table is empty. Refuses to run if any user already exists so it cannot
/// be used as a backdoor after the system is in use.
///
/// Usage:
/// dotnet VigilCareClinicalAPI.dll create-admin --username &lt;u&gt; --password &lt;p&gt; --display-name "&lt;n&gt;"
/// </summary>
public static class CreateAdminCommand
{
public static async Task<int> RunAsync(IServiceProvider services, string[] args)
{
var username = RequireArg(args, "--username");
var password = RequireArg(args, "--password");
var displayName = RequireArg(args, "--display-name");
if (username.Length > 100)
{
Console.Error.WriteLine("Username must be 100 characters or fewer.");
return 1;
}
if (password.Length < 8)
{
Console.Error.WriteLine("Password must be at least 8 characters.");
return 1;
}
if (displayName.Length > 200)
{
Console.Error.WriteLine("Display name must be 200 characters or fewer.");
return 1;
}
using var scope = services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
if (await db.ClinicalUsers.AnyAsync())
{
Console.Error.WriteLine(
"Refusing to create admin: ClinicalUsers already contains at least one row. " +
"create-admin is a one-shot bootstrap and cannot be used as a backdoor.");
return 1;
}
var user = new ClinicalUser
{
Id = Guid.NewGuid(),
Username = username.Trim(),
PasswordHash = BCrypt.Net.BCrypt.HashPassword(password),
DisplayName = displayName.Trim(),
Role = ClinicalRole.Admin,
IsActive = true,
CreatedAt = DateTimeOffset.UtcNow,
};
db.ClinicalUsers.Add(user);
await db.SaveChangesAsync();
Console.WriteLine($"Created admin user '{user.Username}' (id={user.Id}).");
return 0;
}
private static string RequireArg(string[] args, string name)
{
for (var i = 0; i < args.Length - 1; i++)
{
if (string.Equals(args[i], name, StringComparison.OrdinalIgnoreCase))
{
var value = args[i + 1];
if (string.IsNullOrWhiteSpace(value) || value.StartsWith("--", StringComparison.Ordinal))
break;
return value;
}
}
throw new ArgumentException($"Missing required argument: {name}");
}
}