feat: add permanent trust functionality for devices with corresponding API and UI updates

This commit is contained in:
shuaiplus
2026-05-12 18:01:04 +08:00
parent 83a1fc2376
commit 2685741386
15 changed files with 140 additions and 2 deletions
+2
View File
@@ -116,6 +116,7 @@ export interface AppMainRoutesProps {
onSaveDomainRules: (customEquivalentDomains: CustomEquivalentDomain[], excludedGlobalEquivalentDomains: number[]) => Promise<void>;
onRenameAuthorizedDevice: (device: AuthorizedDevice, name: string) => Promise<void>;
onRevokeDeviceTrust: (device: AuthorizedDevice) => void;
onTrustDevicePermanently: (device: AuthorizedDevice) => void;
onRemoveDevice: (device: AuthorizedDevice) => void;
onRevokeAllDeviceTrust: () => void;
onRemoveAllDevices: () => void;
@@ -322,6 +323,7 @@ export default function AppMainRoutes(props: AppMainRoutesProps) {
onRefresh={() => void props.onRefreshAuthorizedDevices()}
onRenameDevice={props.onRenameAuthorizedDevice}
onRevokeTrust={props.onRevokeDeviceTrust}
onTrustPermanently={props.onTrustDevicePermanently}
onRemoveDevice={props.onRemoveDevice}
onRevokeAll={props.onRevokeAllDeviceTrust}
onRemoveAll={props.onRemoveAllDevices}
+18 -2
View File
@@ -1,5 +1,5 @@
import { useState } from 'preact/hooks';
import { Clock3, Pencil, RefreshCw, ShieldOff, Trash2 } from 'lucide-preact';
import { Clock3, Pencil, RefreshCw, ShieldCheck, ShieldOff, Trash2 } from 'lucide-preact';
import ConfirmDialog from '@/components/ConfirmDialog';
import LoadingState from '@/components/LoadingState';
import type { AuthorizedDevice } from '@/lib/types';
@@ -12,6 +12,7 @@ interface SecurityDevicesPageProps {
onRefresh: () => void;
onRenameDevice: (device: AuthorizedDevice, name: string) => Promise<void>;
onRevokeTrust: (device: AuthorizedDevice) => void;
onTrustPermanently: (device: AuthorizedDevice) => void;
onRemoveDevice: (device: AuthorizedDevice) => void;
onRevokeAll: () => void;
onRemoveAll: () => void;
@@ -24,6 +25,12 @@ function formatDateTime(value: string | null | undefined): string {
return date.toLocaleString();
}
function isPermanentTrust(value: string | null | undefined): boolean {
if (!value) return false;
const date = new Date(value);
return !Number.isNaN(date.getTime()) && date.getUTCFullYear() >= 2099;
}
function mapDeviceTypeName(type: number): string {
switch (type) {
case 0: return t('txt_android');
@@ -135,7 +142,7 @@ export default function SecurityDevicesPage(props: SecurityDevicesPageProps) {
{device.trusted ? (
<div className="trusted-cell">
<Clock3 size={13} />
<span>{formatDateTime(device.trustedUntil)}</span>
<span>{isPermanentTrust(device.trustedUntil) ? t('txt_permanent_trust') : formatDateTime(device.trustedUntil)}</span>
</div>
) : (
<span className="muted-inline">{t('txt_not_trusted')}</span>
@@ -152,6 +159,15 @@ export default function SecurityDevicesPage(props: SecurityDevicesPageProps) {
<ShieldOff size={14} className="btn-icon" />
{t('txt_untrust')}
</button>
<button
type="button"
className="btn btn-secondary small"
disabled={!device.trusted || !device.trustedUntil || isPermanentTrust(device.trustedUntil)}
onClick={() => props.onTrustPermanently(device)}
>
<ShieldCheck size={14} className="btn-icon" />
{t('txt_trust_permanently')}
</button>
<button
type="button"
className="btn btn-secondary small"