mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-09 15:58:34 +00:00
13f8e8cd59
Introduces reusable SSH identities for authentication, allowing hosts to reference centralized identity records containing usernames, passwords, and key/certificate associations. Updates connection, SFTP, terminal, and sync logic to resolve credentials via identities, improving credential management and reuse. Enhances UI for identity selection, editing, and deletion. Updates localization for related identity and credential strings. Ensures identities are included in sync and vault persistence. Streamlines SSH connection attempts by preferring key-based auth with fallback to password, both for host and identity-based authentication. Facilitates safer, more flexible credential management across the application.
83 lines
2.9 KiB
TypeScript
83 lines
2.9 KiB
TypeScript
/**
|
|
* Identity Card component for displaying saved identities
|
|
*/
|
|
|
|
import { Pencil,User } from 'lucide-react';
|
|
import React from 'react';
|
|
import { useI18n } from '../../application/i18n/I18nProvider';
|
|
import { cn } from '../../lib/utils';
|
|
import { Identity } from '../../types';
|
|
import { Button } from '../ui/button';
|
|
|
|
interface IdentityCardProps {
|
|
identity: Identity;
|
|
viewMode: 'grid' | 'list';
|
|
isSelected: boolean;
|
|
onClick: () => void;
|
|
}
|
|
|
|
export const IdentityCard: React.FC<IdentityCardProps> = ({
|
|
identity,
|
|
viewMode,
|
|
isSelected,
|
|
onClick,
|
|
}) => {
|
|
const { t } = useI18n();
|
|
|
|
const hasPassword = !!identity.password;
|
|
const hasKey = !!identity.keyId;
|
|
const keyKind = identity.authMethod === 'certificate' ? 'certificate' : 'key';
|
|
|
|
const summary = hasPassword && hasKey
|
|
? (keyKind === 'certificate'
|
|
? t('keychain.identity.summary.passwordAndCertificate')
|
|
: t('keychain.identity.summary.passwordAndKey'))
|
|
: hasKey
|
|
? (keyKind === 'certificate'
|
|
? t('keychain.identity.summary.certificate')
|
|
: t('keychain.identity.summary.key'))
|
|
: hasPassword
|
|
? t('keychain.identity.summary.password')
|
|
: t('keychain.identity.summary.none');
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"group cursor-pointer",
|
|
viewMode === 'grid'
|
|
? "soft-card elevate rounded-xl h-[68px] px-3 py-2"
|
|
: "h-14 px-3 py-2 hover:bg-secondary/60 rounded-lg transition-colors",
|
|
isSelected && "ring-2 ring-primary"
|
|
)}
|
|
onClick={onClick}
|
|
>
|
|
<div className="flex items-center gap-3 h-full">
|
|
<div className="h-11 w-11 rounded-xl bg-green-500/15 text-green-500 flex items-center justify-center">
|
|
<User size={18} />
|
|
</div>
|
|
<div className="min-w-0 flex-1">
|
|
<div className="text-sm font-semibold truncate">{identity.label || 'Add a label...'}</div>
|
|
<div className="text-[11px] font-mono text-muted-foreground truncate">
|
|
{summary}
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
{viewMode === 'list' && (
|
|
<Button
|
|
size="icon"
|
|
variant="ghost"
|
|
className="h-8 w-8"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onClick();
|
|
}}
|
|
>
|
|
<Pencil size={14} />
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|