Vectrum
Sign In
112 lines · 4.3 KB · TypeScript
import type { Metadata } from "next";
import { Accordion, Label as MuLabel } from "@unlocalhosted/metalui";
import { deleteSshKeyAction } from "@/app/actions/settings";
import { ConfirmButton } from "@/components/confirm-button";
import { KeyIcon } from "@/components/icons";
import { AccordionItem, AccordionPanel, AccordionTrigger, SettingsRow, SettingsSection } from "@/components/metal";
import { CopyIconButton } from "@/components/settings-fields";
import { ListRow, PlateSection, RowGlyph } from "@/components/settings-plate";
import { Alert, Blankslate, Box, Label, PageTitle } from "@/components/ui";
import { requireViewer } from "@/lib/auth";
import { formatDate, timeAgo } from "@/lib/format";
import { listSshKeys } from "@/lib/keys";
import { serverHost } from "@/lib/request-origin";
import { SSH_ENABLED, sshTestCommand } from "@/lib/ssh-config";
import { AddKeyForm } from "./form";

export const metadata: Metadata = { title: "SSH keys" };

export default async function KeysPage() {
  const viewer = await requireViewer();
  const keys = listSshKeys(viewer.id);
  const test = sshTestCommand(await serverHost());

  return (
    <div>
      <PageTitle
        title="SSH keys"
        description="Keys associated with your account let you clone and push over SSH without a password."
      />
      <div className="flex flex-col gap-7">
        {!SSH_ENABLED && <Alert>The SSH server is disabled on this instance (SSH_DISABLED=1).</Alert>}

        {keys.length === 0 ? (
          <PlateSection title="Your keys">
            <Blankslate icon={<KeyIcon />} title="No SSH keys yet">
              Add a key below to push with <code className="font-mono">git@</code> URLs.
            </Blankslate>
          </PlateSection>
        ) : (
          <SettingsSection title={`Your keys · ${keys.length}`}>
            {keys.map((key) => (
              <ListRow
                key={key.id}
                lead={
                  <RowGlyph>
                    <KeyIcon />
                  </RowGlyph>
                }
                title={
                  <>
                    <span className="truncate">{key.title}</span>
                    <Label>{key.keyType.replace("ssh-", "").replace("ecdsa-sha2-", "ecdsa ")}</Label>
                  </>
                }
                meta={
                  <>
                    <code className="block truncate font-mono text-[11.5px] leading-4 text-ink2">{key.fingerprint}</code>
                    <MuLabel variant="engraved">
                      Added <b>{formatDate(key.createdAt)}</b> ·{" "}
                      {key.lastUsedAt ? (
                        <>
                          Last used <b>{timeAgo(key.lastUsedAt)}</b>
                        </>
                      ) : (
                        "Never used"
                      )}
                    </MuLabel>
                  </>
                }
              >
                <ConfirmButton
                  action={deleteSshKeyAction.bind(null, key.id)}
                  message={`Delete the SSH key “${key.title}”? Any machine using it will lose access.`}
                  variant="default"
                >
                  Delete Key
                </ConfirmButton>
              </ListRow>
            ))}
          </SettingsSection>
        )}

        <SettingsSection title="Connection">
          <SettingsRow
            name="Test your connection"
            detail={<code className="block truncate font-mono text-[11.5px]">{test}</code>}
          >
            <CopyIconButton text={test} label="Copy command" />
          </SettingsRow>
        </SettingsSection>

        <PlateSection title="Add new SSH key">
          <AddKeyForm />
        </PlateSection>

        <Box className="p-1.5">
          <Accordion>
            <AccordionItem>
              <AccordionTrigger>Don’t have a key yet?</AccordionTrigger>
              <AccordionPanel>
                <p>Generate one and paste the contents of the <code className="font-mono">.pub</code> file:</p>
                <pre className="well mt-2 overflow-x-auto rounded-row px-3.5 py-3 font-mono text-[12px] leading-5 text-ink">
                  ssh-keygen -t ed25519 -C &quot;{viewer.email}&quot;{"\n"}cat ~/.ssh/id_ed25519.pub
                </pre>
              </AccordionPanel>
            </AccordionItem>
          </Accordion>
        </Box>
      </div>
    </div>
  );
}