201 lines
6.1 KiB
C#
201 lines
6.1 KiB
C#
|
|
using VoiceCat.Interop;
|
||
|
|
|
||
|
|
namespace VoiceCat.App.Forms;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Admin account management: list, create, reset password, and delete server accounts.
|
||
|
|
/// </summary>
|
||
|
|
public sealed class AccountsDialog : Form
|
||
|
|
{
|
||
|
|
private readonly VoiceCatClient _client;
|
||
|
|
private readonly ListView _lvAccounts;
|
||
|
|
private readonly Button _btnRefresh;
|
||
|
|
private readonly Button _btnAdd;
|
||
|
|
private readonly Button _btnResetPassword;
|
||
|
|
private readonly Button _btnDelete;
|
||
|
|
|
||
|
|
public AccountsDialog(VoiceCatClient client)
|
||
|
|
{
|
||
|
|
_client = client;
|
||
|
|
|
||
|
|
Text = "Server accounts";
|
||
|
|
FormBorderStyle = FormBorderStyle.Sizable;
|
||
|
|
StartPosition = FormStartPosition.CenterParent;
|
||
|
|
AutoScaleMode = AutoScaleMode.Font;
|
||
|
|
ClientSize = new Size(560, 360);
|
||
|
|
MinimumSize = new Size(420, 260);
|
||
|
|
|
||
|
|
_lvAccounts = new ListView
|
||
|
|
{
|
||
|
|
View = View.Details,
|
||
|
|
FullRowSelect = true,
|
||
|
|
GridLines = true,
|
||
|
|
Dock = DockStyle.Fill,
|
||
|
|
TabIndex = 0,
|
||
|
|
};
|
||
|
|
_lvAccounts.Columns.Add("Username", 160);
|
||
|
|
_lvAccounts.Columns.Add("Admin", 50);
|
||
|
|
_lvAccounts.Columns.Add("Created", 130);
|
||
|
|
_lvAccounts.Columns.Add("Last login", 130);
|
||
|
|
_lvAccounts.SelectedIndexChanged += (_, _) => UpdateButtons();
|
||
|
|
|
||
|
|
var pnlButtons = new Panel
|
||
|
|
{
|
||
|
|
Dock = DockStyle.Bottom,
|
||
|
|
Height = 44,
|
||
|
|
};
|
||
|
|
|
||
|
|
_btnRefresh = new Button
|
||
|
|
{
|
||
|
|
Text = "&Refresh",
|
||
|
|
Location = new Point(12, 9),
|
||
|
|
Size = new Size(75, 27),
|
||
|
|
TabIndex = 1,
|
||
|
|
};
|
||
|
|
_btnRefresh.Click += (_, _) => RefreshList();
|
||
|
|
|
||
|
|
_btnAdd = new Button
|
||
|
|
{
|
||
|
|
Text = "&Add...",
|
||
|
|
Location = new Point(100, 9),
|
||
|
|
Size = new Size(75, 27),
|
||
|
|
TabIndex = 2,
|
||
|
|
};
|
||
|
|
_btnAdd.Click += (_, _) => AddAccount();
|
||
|
|
|
||
|
|
_btnResetPassword = new Button
|
||
|
|
{
|
||
|
|
Text = "&Reset password...",
|
||
|
|
Location = new Point(188, 9),
|
||
|
|
Size = new Size(120, 27),
|
||
|
|
TabIndex = 3,
|
||
|
|
Enabled = false,
|
||
|
|
};
|
||
|
|
_btnResetPassword.Click += (_, _) => ResetPassword();
|
||
|
|
|
||
|
|
_btnDelete = new Button
|
||
|
|
{
|
||
|
|
Text = "&Delete",
|
||
|
|
Location = new Point(320, 9),
|
||
|
|
Size = new Size(75, 27),
|
||
|
|
TabIndex = 4,
|
||
|
|
Enabled = false,
|
||
|
|
};
|
||
|
|
_btnDelete.Click += (_, _) => DeleteAccount();
|
||
|
|
|
||
|
|
pnlButtons.Controls.AddRange([_btnRefresh, _btnAdd, _btnResetPassword, _btnDelete]);
|
||
|
|
|
||
|
|
Controls.Add(_lvAccounts);
|
||
|
|
Controls.Add(pnlButtons);
|
||
|
|
|
||
|
|
Load += (_, _) => RefreshList();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void UpdateButtons()
|
||
|
|
{
|
||
|
|
bool selected = _lvAccounts.SelectedItems.Count > 0;
|
||
|
|
_btnResetPassword.Enabled = selected;
|
||
|
|
_btnDelete.Enabled = selected;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void RefreshList()
|
||
|
|
{
|
||
|
|
_client.RequestAccountList();
|
||
|
|
// The event will come back asynchronously; poll briefly for the list.
|
||
|
|
var deadline = DateTime.UtcNow.AddSeconds(2);
|
||
|
|
List<AccountInfo>? accounts = null;
|
||
|
|
while (DateTime.UtcNow < deadline)
|
||
|
|
{
|
||
|
|
_client.PumpEvents();
|
||
|
|
accounts = _client.ListAccounts();
|
||
|
|
if (accounts.Count > 0) break;
|
||
|
|
Thread.Sleep(30);
|
||
|
|
}
|
||
|
|
accounts ??= _client.ListAccounts();
|
||
|
|
|
||
|
|
_lvAccounts.BeginUpdate();
|
||
|
|
_lvAccounts.Items.Clear();
|
||
|
|
foreach (var a in accounts.OrderBy(a => a.Username))
|
||
|
|
{
|
||
|
|
var item = new ListViewItem(a.Username);
|
||
|
|
item.SubItems.Add(a.IsAdmin ? "Yes" : "No");
|
||
|
|
item.SubItems.Add(FormatDate(a.CreatedAtUnixMs));
|
||
|
|
item.SubItems.Add(FormatDate(a.LastLoginUnixMs));
|
||
|
|
_lvAccounts.Items.Add(item);
|
||
|
|
}
|
||
|
|
_lvAccounts.EndUpdate();
|
||
|
|
UpdateButtons();
|
||
|
|
}
|
||
|
|
|
||
|
|
private static string FormatDate(ulong ms)
|
||
|
|
{
|
||
|
|
if (ms == 0) return "—";
|
||
|
|
try
|
||
|
|
{
|
||
|
|
return DateTimeOffset.FromUnixTimeMilliseconds((long)ms).LocalDateTime.ToString("g");
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
return "—";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void AddAccount()
|
||
|
|
{
|
||
|
|
using var userDlg = new InputDialog("Add account", "&Username:");
|
||
|
|
if (userDlg.ShowDialog(this) != DialogResult.OK || string.IsNullOrWhiteSpace(userDlg.TextValue))
|
||
|
|
return;
|
||
|
|
|
||
|
|
using var pwDlg = new PasswordPromptDialog($"Password for {userDlg.TextValue}:");
|
||
|
|
if (pwDlg.ShowDialog(this) != DialogResult.OK || string.IsNullOrEmpty(pwDlg.Password))
|
||
|
|
return;
|
||
|
|
|
||
|
|
var r = _client.CreateAccount(userDlg.TextValue, pwDlg.Password);
|
||
|
|
if (r != VcResult.Ok)
|
||
|
|
{
|
||
|
|
MessageBox.Show(this, $"Create account failed: {r}", "VoiceCat",
|
||
|
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
RefreshList();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void ResetPassword()
|
||
|
|
{
|
||
|
|
if (_lvAccounts.SelectedItems.Count == 0) return;
|
||
|
|
string username = _lvAccounts.SelectedItems[0].Text;
|
||
|
|
|
||
|
|
using var dlg = new PasswordPromptDialog($"New password for {username}:");
|
||
|
|
if (dlg.ShowDialog(this) != DialogResult.OK || string.IsNullOrEmpty(dlg.Password))
|
||
|
|
return;
|
||
|
|
|
||
|
|
var r = _client.ResetPassword(username, dlg.Password);
|
||
|
|
if (r != VcResult.Ok)
|
||
|
|
{
|
||
|
|
MessageBox.Show(this, $"Reset password failed: {r}", "VoiceCat",
|
||
|
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
RefreshList();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DeleteAccount()
|
||
|
|
{
|
||
|
|
if (_lvAccounts.SelectedItems.Count == 0) return;
|
||
|
|
string username = _lvAccounts.SelectedItems[0].Text;
|
||
|
|
|
||
|
|
var confirm = MessageBox.Show(this, $"Delete account '{username}'?",
|
||
|
|
"VoiceCat", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||
|
|
if (confirm != DialogResult.Yes) return;
|
||
|
|
|
||
|
|
var r = _client.DeleteAccount(username);
|
||
|
|
if (r != VcResult.Ok)
|
||
|
|
{
|
||
|
|
MessageBox.Show(this, $"Delete account failed: {r}", "VoiceCat",
|
||
|
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
RefreshList();
|
||
|
|
}
|
||
|
|
}
|