mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
refactor: enhance manual chunking in Vite config for better code splitting
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
import { useMemo } from 'preact/hooks';
|
||||
import {
|
||||
changeMasterPassword,
|
||||
deleteAllAuthorizedDevices,
|
||||
deleteAuthorizedDevice,
|
||||
deriveLoginHash,
|
||||
getCurrentDeviceIdentifier,
|
||||
getTotpRecoveryCode,
|
||||
revokeAuthorizedDeviceTrust,
|
||||
revokeAllAuthorizedDeviceTrust,
|
||||
setTotp,
|
||||
} from '@/lib/api/auth';
|
||||
import { t } from '@/lib/i18n';
|
||||
import type { AppConfirmState } from '@/components/AppGlobalOverlays';
|
||||
import type { AuthedFetch } from '@/lib/api/shared';
|
||||
import type { AuthorizedDevice, Profile } from '@/lib/types';
|
||||
|
||||
type Notify = (type: 'success' | 'error' | 'warning', text: string) => void;
|
||||
|
||||
interface UseAccountSecurityActionsOptions {
|
||||
authedFetch: AuthedFetch;
|
||||
profile: Profile | null;
|
||||
defaultKdfIterations: number;
|
||||
disableTotpPassword: string;
|
||||
clearDisableTotpDialog: () => void;
|
||||
onPromptLogout: () => void;
|
||||
onLogoutNow: () => void;
|
||||
onNotify: Notify;
|
||||
onSetConfirm: (next: AppConfirmState | null) => void;
|
||||
refetchTotpStatus: () => Promise<unknown>;
|
||||
refetchAuthorizedDevices: () => Promise<unknown>;
|
||||
}
|
||||
|
||||
export default function useAccountSecurityActions(options: UseAccountSecurityActionsOptions) {
|
||||
const {
|
||||
authedFetch,
|
||||
profile,
|
||||
defaultKdfIterations,
|
||||
disableTotpPassword,
|
||||
clearDisableTotpDialog,
|
||||
onPromptLogout,
|
||||
onLogoutNow,
|
||||
onNotify,
|
||||
onSetConfirm,
|
||||
refetchTotpStatus,
|
||||
refetchAuthorizedDevices,
|
||||
} = options;
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
async changePassword(currentPassword: string, nextPassword: string, nextPassword2: string) {
|
||||
if (!profile) return;
|
||||
if (!currentPassword || !nextPassword) {
|
||||
onNotify('error', t('txt_current_new_password_is_required'));
|
||||
return;
|
||||
}
|
||||
if (nextPassword.length < 12) {
|
||||
onNotify('error', t('txt_new_password_must_be_at_least_12_chars'));
|
||||
return;
|
||||
}
|
||||
if (nextPassword !== nextPassword2) {
|
||||
onNotify('error', t('txt_new_passwords_do_not_match'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await changeMasterPassword(authedFetch, {
|
||||
email: profile.email,
|
||||
currentPassword,
|
||||
newPassword: nextPassword,
|
||||
currentIterations: defaultKdfIterations,
|
||||
profileKey: profile.key,
|
||||
});
|
||||
onPromptLogout();
|
||||
onNotify('success', t('txt_master_password_changed_please_login_again'));
|
||||
} catch (error) {
|
||||
onNotify('error', error instanceof Error ? error.message : t('txt_change_password_failed'));
|
||||
}
|
||||
},
|
||||
|
||||
async enableTotp(secret: string, token: string) {
|
||||
if (!secret.trim() || !token.trim()) {
|
||||
const error = new Error(t('txt_secret_and_code_are_required'));
|
||||
onNotify('error', error.message);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
await setTotp(authedFetch, { enabled: true, secret: secret.trim(), token: token.trim() });
|
||||
onNotify('success', t('txt_totp_enabled'));
|
||||
} catch (error) {
|
||||
onNotify('error', error instanceof Error ? error.message : t('txt_enable_totp_failed'));
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async disableTotp() {
|
||||
if (!profile) return;
|
||||
if (!disableTotpPassword) {
|
||||
onNotify('error', t('txt_please_input_master_password'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const derived = await deriveLoginHash(profile.email, disableTotpPassword, defaultKdfIterations);
|
||||
await setTotp(authedFetch, { enabled: false, masterPasswordHash: derived.hash });
|
||||
if (profile.id) localStorage.removeItem(`nodewarden.totp.secret.${profile.id}`);
|
||||
clearDisableTotpDialog();
|
||||
await refetchTotpStatus();
|
||||
onNotify('success', t('txt_totp_disabled'));
|
||||
} catch (error) {
|
||||
onNotify('error', error instanceof Error ? error.message : t('txt_disable_totp_failed'));
|
||||
}
|
||||
},
|
||||
|
||||
async getRecoveryCode(masterPassword: string): Promise<string> {
|
||||
if (!profile) throw new Error(t('txt_profile_unavailable'));
|
||||
const normalized = String(masterPassword || '');
|
||||
if (!normalized) throw new Error(t('txt_master_password_is_required'));
|
||||
const derived = await deriveLoginHash(profile.email, normalized, defaultKdfIterations);
|
||||
const code = await getTotpRecoveryCode(authedFetch, derived.hash);
|
||||
if (!code) throw new Error(t('txt_recovery_code_is_empty'));
|
||||
return code;
|
||||
},
|
||||
|
||||
async refreshAuthorizedDevices() {
|
||||
await refetchAuthorizedDevices();
|
||||
},
|
||||
|
||||
openRevokeDeviceTrust(device: AuthorizedDevice) {
|
||||
onSetConfirm({
|
||||
title: t('txt_revoke_device_authorization'),
|
||||
message: t('txt_revoke_30_day_totp_trust_for_name', { name: device.name }),
|
||||
danger: true,
|
||||
onConfirm: () => {
|
||||
onSetConfirm(null);
|
||||
void (async () => {
|
||||
await revokeAuthorizedDeviceTrust(authedFetch, device.identifier);
|
||||
await refetchAuthorizedDevices();
|
||||
onNotify('success', t('txt_device_authorization_revoked'));
|
||||
})();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
openRemoveDevice(device: AuthorizedDevice) {
|
||||
onSetConfirm({
|
||||
title: t('txt_remove_device'),
|
||||
message: t('txt_remove_device_and_sign_out_name', { name: device.name }),
|
||||
danger: true,
|
||||
onConfirm: () => {
|
||||
onSetConfirm(null);
|
||||
void (async () => {
|
||||
await deleteAuthorizedDevice(authedFetch, device.identifier);
|
||||
if (device.identifier === getCurrentDeviceIdentifier()) {
|
||||
onNotify('success', t('txt_device_removed'));
|
||||
onLogoutNow();
|
||||
return;
|
||||
}
|
||||
await refetchAuthorizedDevices();
|
||||
onNotify('success', t('txt_device_removed'));
|
||||
})();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
openRevokeAllDeviceTrust() {
|
||||
onSetConfirm({
|
||||
title: t('txt_revoke_all_trusted_devices'),
|
||||
message: t('txt_revoke_30_day_totp_trust_from_all_devices'),
|
||||
danger: true,
|
||||
onConfirm: () => {
|
||||
onSetConfirm(null);
|
||||
void (async () => {
|
||||
await revokeAllAuthorizedDeviceTrust(authedFetch);
|
||||
await refetchAuthorizedDevices();
|
||||
onNotify('success', t('txt_all_device_authorizations_revoked'));
|
||||
})();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
openRemoveAllDevices() {
|
||||
onSetConfirm({
|
||||
title: t('txt_remove_all_devices'),
|
||||
message: t('txt_remove_all_devices_and_sign_out_all_sessions'),
|
||||
danger: true,
|
||||
onConfirm: () => {
|
||||
onSetConfirm(null);
|
||||
void (async () => {
|
||||
await deleteAllAuthorizedDevices(authedFetch);
|
||||
onNotify('success', t('txt_all_devices_removed'));
|
||||
onLogoutNow();
|
||||
})();
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
[
|
||||
authedFetch,
|
||||
clearDisableTotpDialog,
|
||||
defaultKdfIterations,
|
||||
disableTotpPassword,
|
||||
onLogoutNow,
|
||||
onNotify,
|
||||
onPromptLogout,
|
||||
onSetConfirm,
|
||||
profile,
|
||||
refetchAuthorizedDevices,
|
||||
refetchTotpStatus,
|
||||
]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user