mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
feat(devices): add functionality to delete all authorized devices
This commit is contained in:
+25
-1
@@ -43,6 +43,7 @@ import {
|
||||
getPreloginKdfConfig,
|
||||
getProfile,
|
||||
getAuthorizedDevices,
|
||||
getCurrentDeviceIdentifier,
|
||||
getSetupStatus,
|
||||
getSends,
|
||||
getTotpStatus,
|
||||
@@ -60,6 +61,7 @@ import {
|
||||
saveSession,
|
||||
setTotp,
|
||||
setUserStatus,
|
||||
deleteAllAuthorizedDevices,
|
||||
deleteAuthorizedDevice,
|
||||
uploadCipherAttachment,
|
||||
updateCipher,
|
||||
@@ -969,10 +971,21 @@ export default function App() {
|
||||
|
||||
async function removeDeviceAction(device: AuthorizedDevice) {
|
||||
await deleteAuthorizedDevice(authedFetch, device.identifier);
|
||||
if (device.identifier === getCurrentDeviceIdentifier()) {
|
||||
pushToast('success', t('txt_device_removed'));
|
||||
logoutNow();
|
||||
return;
|
||||
}
|
||||
await authorizedDevicesQuery.refetch();
|
||||
pushToast('success', t('txt_device_removed'));
|
||||
}
|
||||
|
||||
async function removeAllDevicesAction() {
|
||||
await deleteAllAuthorizedDevices(authedFetch);
|
||||
pushToast('success', t('txt_all_devices_removed'));
|
||||
logoutNow();
|
||||
}
|
||||
|
||||
async function createVaultItem(draft: VaultDraft, attachments: File[] = []) {
|
||||
if (!session) return;
|
||||
try {
|
||||
@@ -2004,7 +2017,7 @@ export default function App() {
|
||||
onRemoveDevice={(device) => {
|
||||
setConfirm({
|
||||
title: t('txt_remove_device'),
|
||||
message: t('txt_remove_device_name_and_clear_its_2fa_trust', { name: device.name }),
|
||||
message: t('txt_remove_device_and_sign_out_name', { name: device.name }),
|
||||
danger: true,
|
||||
onConfirm: () => {
|
||||
setConfirm(null);
|
||||
@@ -2023,6 +2036,17 @@ export default function App() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
onRemoveAll={() => {
|
||||
setConfirm({
|
||||
title: t('txt_remove_all_devices'),
|
||||
message: t('txt_remove_all_devices_and_sign_out_all_sessions'),
|
||||
danger: true,
|
||||
onConfirm: () => {
|
||||
setConfirm(null);
|
||||
void removeAllDevicesAction();
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Route>
|
||||
|
||||
@@ -9,6 +9,7 @@ interface SecurityDevicesPageProps {
|
||||
onRevokeTrust: (device: AuthorizedDevice) => void;
|
||||
onRemoveDevice: (device: AuthorizedDevice) => void;
|
||||
onRevokeAll: () => void;
|
||||
onRemoveAll: () => void;
|
||||
}
|
||||
|
||||
function formatDateTime(value: string | null | undefined): string {
|
||||
@@ -47,7 +48,7 @@ export default function SecurityDevicesPage(props: SecurityDevicesPageProps) {
|
||||
<div>
|
||||
<h3 style={{ margin: 0 }}>{t('txt_device_management')}</h3>
|
||||
<div className="muted-inline" style={{ marginTop: 4 }}>
|
||||
{t('txt_manage_authorized_devices_and_30_day_totp_trusted_sessions')}
|
||||
{t('txt_manage_device_sessions_and_30_day_totp_trusted_sessions')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="actions">
|
||||
@@ -59,6 +60,10 @@ export default function SecurityDevicesPage(props: SecurityDevicesPageProps) {
|
||||
<ShieldOff size={14} className="btn-icon" />
|
||||
{t('txt_revoke_all_trusted')}
|
||||
</button>
|
||||
<button type="button" className="btn btn-danger small" onClick={props.onRemoveAll}>
|
||||
<Trash2 size={14} className="btn-icon" />
|
||||
{t('txt_remove_all_devices')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -119,6 +119,10 @@ function getOrCreateDeviceIdentifier(): string {
|
||||
return next;
|
||||
}
|
||||
|
||||
export function getCurrentDeviceIdentifier(): string {
|
||||
return (localStorage.getItem(DEVICE_IDENTIFIER_KEY) || '').trim();
|
||||
}
|
||||
|
||||
function guessDeviceName(): string {
|
||||
const ua = (typeof navigator !== 'undefined' ? navigator.userAgent : '').toLowerCase();
|
||||
const platform = (typeof navigator !== 'undefined' ? navigator.platform : '').trim();
|
||||
@@ -772,6 +776,13 @@ export async function deleteAuthorizedDevice(
|
||||
if (!resp.ok) throw new Error('Failed to remove device');
|
||||
}
|
||||
|
||||
export async function deleteAllAuthorizedDevices(
|
||||
authedFetch: (input: string, init?: RequestInit) => Promise<Response>
|
||||
): Promise<void> {
|
||||
const resp = await authedFetch('/api/devices', { method: 'DELETE' });
|
||||
if (!resp.ok) throw new Error('Failed to remove all devices');
|
||||
}
|
||||
|
||||
export async function listAdminUsers(authedFetch: (input: string, init?: RequestInit) => Promise<Response>): Promise<AdminUser[]> {
|
||||
const resp = await authedFetch('/api/admin/users');
|
||||
if (!resp.ok) throw new Error('Failed to load users');
|
||||
|
||||
@@ -232,6 +232,7 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
txt_login_success: "Login success",
|
||||
txt_macos_desktop: "macOS Desktop",
|
||||
txt_manage_authorized_devices_and_30_day_totp_trusted_sessions: "Manage authorized devices and 30-day TOTP trusted sessions.",
|
||||
txt_manage_device_sessions_and_30_day_totp_trusted_sessions: "Manage device sessions and 30-day TOTP trusted sessions.",
|
||||
txt_master_password: "Master Password",
|
||||
txt_master_password_changed_please_login_again: "Master password changed. Please login again.",
|
||||
txt_master_password_is_required: "Master password is required",
|
||||
@@ -301,7 +302,11 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
txt_ignore: "Ignore",
|
||||
txt_remove_device: "Remove device",
|
||||
txt_remove_device_2: "Remove Device",
|
||||
txt_remove_all_devices: "Remove all devices",
|
||||
txt_remove_all_devices_and_clear_all_2fa_trust: "Remove all devices and clear all 2FA trust?",
|
||||
txt_remove_all_devices_and_sign_out_all_sessions: "Remove all devices, clear all trust, and sign out every device?",
|
||||
txt_remove_device_name_and_clear_its_2fa_trust: "Remove device \"{name}\" and clear its 2FA trust?",
|
||||
txt_remove_device_and_sign_out_name: "Remove device \"{name}\", clear its trust, and sign it out?",
|
||||
txt_reveal: "Reveal",
|
||||
txt_revoke: "Revoke",
|
||||
txt_revoke_30_day_totp_trust_for_name: "Revoke 30-day TOTP trust for \"{name}\"?",
|
||||
@@ -384,6 +389,7 @@ const messages: Record<Locale, Record<string, string>> = {
|
||||
txt_unlock_vault: "Unlock Vault",
|
||||
txt_unignore: "Unignore",
|
||||
txt_unlocked: "Unlocked",
|
||||
txt_all_devices_removed: "All devices removed",
|
||||
txt_update_item_failed: "Update item failed",
|
||||
txt_update_send_failed: "Update send failed",
|
||||
txt_use_recovery_code: "Use Recovery Code",
|
||||
@@ -610,6 +616,7 @@ const zhCNOverrides: Record<string, string> = {
|
||||
txt_copy_secret: '复制密钥',
|
||||
txt_this_is_a_one_time_code_after_it_is_used_a_new_code_is_generated_automatically: '这是一次性恢复代码,使用后将自动生成新的恢复代码。',
|
||||
txt_manage_authorized_devices_and_30_day_totp_trusted_sessions: '管理已授权设备和 30 天 TOTP 受信会话。',
|
||||
txt_manage_device_sessions_and_30_day_totp_trusted_sessions: '管理设备会话和 30 天 TOTP 受信状态。',
|
||||
txt_role: '角色',
|
||||
txt_status: '状态',
|
||||
txt_actions: '操作',
|
||||
@@ -619,6 +626,10 @@ const zhCNOverrides: Record<string, string> = {
|
||||
txt_revoke_30_day_totp_trust_from_all_devices: '确认撤销所有设备的 30 天 TOTP 信任吗?',
|
||||
txt_revoke_30_day_totp_trust_for_name: '确认撤销“{name}”的 30 天 TOTP 信任吗?',
|
||||
txt_remove_device_name_and_clear_its_2fa_trust: '确认移除设备“{name}”并清除其 2FA 信任吗?',
|
||||
txt_remove_all_devices: '移除所有设备',
|
||||
txt_remove_all_devices_and_clear_all_2fa_trust: '确认移除所有设备并清除全部 2FA 信任吗?',
|
||||
txt_remove_all_devices_and_sign_out_all_sessions: '确认移除所有设备、清除全部信任,并让所有设备重新登录吗?',
|
||||
txt_remove_device_and_sign_out_name: '确认移除设备“{name}”、清除其信任,并让它重新登录吗?',
|
||||
txt_role_admin: '管理员',
|
||||
txt_role_user: '用户',
|
||||
txt_status_active: '正常',
|
||||
@@ -766,6 +777,7 @@ const zhCNOverrides: Record<string, string> = {
|
||||
txt_unlock_failed: '解锁失败',
|
||||
txt_unlock_failed_master_password_is_incorrect: '解锁失败,主密码不正确。',
|
||||
txt_unlocked: '已解锁',
|
||||
txt_all_devices_removed: '已移除所有设备',
|
||||
txt_update_item_failed: '更新项目失败',
|
||||
txt_update_send_failed: '更新发送失败',
|
||||
txt_use_your_one_time_recovery_code_to_disable_two_step_verification: '使用一次性恢复代码禁用两步验证。',
|
||||
|
||||
Reference in New Issue
Block a user