using VoiceCat.Interop; namespace VoiceCat.App.Forms; /// /// Admin account management: list, create, reset password, and delete server accounts. /// 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? 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(); } }