Files
Palmr/apps/web/src/app/profile/hooks/use-trusted-devices.ts
Daniel Luiz Alves f3f792e053 feat(auth): enhance trusted device management for 2FA
- Added lastUsedAt timestamp to the TrustedDevice model for tracking device usage.
- Implemented new endpoints for retrieving and removing trusted devices.
- Updated AuthService to manage trusted devices, including methods for getting and removing devices.
- Enhanced the user interface to support trusted device management, including modals for removing devices.
- Added translations for new messages related to trusted devices in multiple languages.
2025-07-09 23:43:57 -03:00

145 lines
4.5 KiB
TypeScript

"use client";
import { useCallback, useEffect, useState } from "react";
import { useTranslations } from "next-intl";
import { toast } from "sonner";
import { getTrustedDevices, removeAllTrustedDevices, removeTrustedDevice } from "@/http/endpoints";
import type { TrustedDevice } from "@/http/endpoints/auth/trusted-devices/types";
export function useTrustedDevices() {
const t = useTranslations();
const [isLoading, setIsLoading] = useState(true);
const [devices, setDevices] = useState<TrustedDevice[]>([]);
const [isRemoveModalOpen, setIsRemoveModalOpen] = useState(false);
const [isRemoveAllModalOpen, setIsRemoveAllModalOpen] = useState(false);
const [deviceToRemove, setDeviceToRemove] = useState<TrustedDevice | null>(null);
const [isRemoving, setIsRemoving] = useState(false);
const loadDevices = useCallback(async () => {
try {
setIsLoading(true);
const response = await getTrustedDevices();
setDevices(response.devices);
} catch (error) {
toast.error(t("twoFactor.trustedDevices.loadFailed"));
console.error("Failed to load trusted devices:", error);
} finally {
setIsLoading(false);
}
}, [t]);
const handleRemoveDevice = useCallback(async (device: TrustedDevice) => {
setDeviceToRemove(device);
setIsRemoveModalOpen(true);
}, []);
const confirmRemoveDevice = useCallback(async () => {
if (!deviceToRemove) return;
try {
setIsRemoving(true);
await removeTrustedDevice({ deviceId: deviceToRemove.id });
toast.success(t("twoFactor.trustedDevices.deviceRemoved"));
await loadDevices();
setIsRemoveModalOpen(false);
setDeviceToRemove(null);
} catch (error) {
toast.error(t("twoFactor.trustedDevices.removeFailed"));
console.error("Failed to remove trusted device:", error);
} finally {
setIsRemoving(false);
}
}, [deviceToRemove, t, loadDevices]);
const handleRemoveAllDevices = useCallback(() => {
setIsRemoveAllModalOpen(true);
}, []);
const confirmRemoveAllDevices = useCallback(async () => {
try {
setIsRemoving(true);
const response = await removeAllTrustedDevices();
toast.success(t("twoFactor.trustedDevices.allDevicesRemoved"));
await loadDevices();
setIsRemoveAllModalOpen(false);
} catch (error) {
toast.error(t("twoFactor.trustedDevices.removeAllFailed"));
console.error("Failed to remove all trusted devices:", error);
} finally {
setIsRemoving(false);
}
}, [t, loadDevices]);
const formatDeviceName = useCallback(
(device: TrustedDevice) => {
const userAgent = device.userAgent;
// Extract browser and OS info from user agent
let deviceInfo = t("twoFactor.deviceNames.unknownDevice");
// Verificação de null safety
if (!userAgent) {
return deviceInfo;
}
if (userAgent.includes("Chrome")) {
deviceInfo = t("twoFactor.deviceNames.browsers.chrome");
} else if (userAgent.includes("Firefox")) {
deviceInfo = t("twoFactor.deviceNames.browsers.firefox");
} else if (userAgent.includes("Safari") && !userAgent.includes("Chrome")) {
deviceInfo = t("twoFactor.deviceNames.browsers.safari");
} else if (userAgent.includes("Edge")) {
deviceInfo = t("twoFactor.deviceNames.browsers.edge");
}
if (userAgent.includes("Windows")) {
deviceInfo += t("twoFactor.deviceNames.platforms.windows");
} else if (userAgent.includes("Mac")) {
deviceInfo += t("twoFactor.deviceNames.platforms.macos");
} else if (userAgent.includes("Linux")) {
deviceInfo += t("twoFactor.deviceNames.platforms.linux");
} else if (userAgent.includes("iPhone")) {
deviceInfo += t("twoFactor.deviceNames.platforms.iphone");
} else if (userAgent.includes("Android")) {
deviceInfo += t("twoFactor.deviceNames.platforms.android");
}
return deviceInfo;
},
[t]
);
const formatDate = useCallback((dateString: string) => {
return new Date(dateString).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
});
}, []);
useEffect(() => {
loadDevices();
}, [loadDevices]);
return {
isLoading,
devices,
isRemoveModalOpen,
isRemoveAllModalOpen,
deviceToRemove,
isRemoving,
setIsRemoveModalOpen,
setIsRemoveAllModalOpen,
handleRemoveDevice,
confirmRemoveDevice,
handleRemoveAllDevices,
confirmRemoveAllDevices,
formatDeviceName,
formatDate,
loadDevices,
};
}