import { useEffect, useState } from "react"; import Dialog, { DialogPanel } from "~/components/dialog"; import Input from "~/components/input"; import Link from "~/components/link"; import Select from "~/components/select"; import Text from "~/components/text"; import Title from "~/components/title"; import TokenList from "~/components/token-list"; import { KNOWN_SSH_ACTIONS, type SshRule } from "~/utils/acl-policy"; interface SshRuleDialogProps { isOpen: boolean; setIsOpen: (isOpen: boolean) => void; rule?: SshRule; sources: string[]; destinations: string[]; onSave: (rule: SshRule) => void; } const EMPTY: SshRule = { action: "accept", src: [], dst: [], users: [], extra: {} }; const SSH_USERS = ["root", "autogroup:nonroot"]; export default function SshRuleDialog({ isOpen, setIsOpen, rule, sources, destinations, onSave, }: SshRuleDialogProps) { const [draft, setDraft] = useState(rule ?? EMPTY); useEffect(() => { if (isOpen) { setDraft(rule ? structuredClone(rule) : structuredClone(EMPTY)); } }, [isOpen, rule]); const isInvalid = draft.src.length === 0 || draft.dst.length === 0 || draft.users.length === 0; return ( { event.preventDefault(); onSave(draft); setIsOpen(false); }} > {rule ? "Edit SSH rule" : "New SSH rule"} SSH rules control Tailscale SSH access between nodes. Read the{" "} Tailscale SSH documentation {" "} for details about check mode. setDraft({ ...draft, checkPeriod: checkPeriod.length > 0 ? checkPeriod : undefined }) } placeholder="12h" value={draft.checkPeriod ?? ""} /> ) : null} ); }