Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/account/ssh-keys/ssh-key-list.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Button, Popconfirm, Typography } from "antd";6import { Map } from "immutable";7import { useIntl } from "react-intl";89import { redux } from "@cocalc/frontend/app-framework";10import {11Gap,12HelpIcon,13Icon,14SettingBox,15TimeAgo,16} from "@cocalc/frontend/components";17import { labels } from "@cocalc/frontend/i18n";18import { CancelText } from "@cocalc/frontend/i18n/components";19import { cmp } from "@cocalc/util/misc";2021interface SSHKeyListProps {22ssh_keys?: Map<string, any>;23project_id?: string;24help?: JSX.Element;25children?: any;26mode?: "project" | "flyout";27}2829// Children are rendered above the list of SSH Keys30// Takes an optional Help string or node to render as a help modal31export const SSHKeyList: React.FC<SSHKeyListProps> = (32props: SSHKeyListProps,33) => {34const intl = useIntl();35const { ssh_keys, project_id, help, children, mode = "project" } = props;36const isFlyout = mode === "flyout";3738function render_header() {39return (40<>41{intl.formatMessage(labels.ssh_keys)} <Gap />42{help && <HelpIcon title="Using SSH Keys">{help}</HelpIcon>}43</>44);45}4647function render_keys() {48if (ssh_keys == null || ssh_keys.size == 0) return;49const v: { date?: Date; fp: string; component: JSX.Element }[] = [];5051ssh_keys?.forEach(52(ssh_key: Map<string, any>, fingerprint: string): void => {53if (!ssh_key) {54return;55}56ssh_key = ssh_key.set("fingerprint", fingerprint);57v.push({58date: ssh_key.get("last_use_date"),59fp: fingerprint,60component: (61<OneSSHKey62ssh_key={ssh_key}63key={fingerprint}64project_id={project_id}65mode={mode}66/>67),68});69},70);71// sort in reverse order by last_use_date, then by fingerprint72v.sort(function (a, b) {73if (a.date != null && b.date != null) {74return -cmp(a.date, b.date);75}76if (a.date && b.date == null) {77return -1;78}79if (b.date && a.date == null) {80return +1;81}82return cmp(a.fp, b.fp);83});84if (isFlyout) {85return <div>{v.map((x) => x.component)}</div>;86} else {87return (88<SettingBox style={{ marginBottom: "0px" }} show_header={false}>89{v.map((x) => x.component)}90</SettingBox>91);92}93}9495function renderBody() {96return (97<>98{children}99{render_keys()}100</>101);102}103104if (isFlyout) {105return renderBody();106} else {107return (108<SettingBox title={render_header()} icon={"list-ul"}>109{renderBody()}110</SettingBox>111);112}113};114115interface OneSSHKeyProps {116ssh_key: Map<string, any>;117project_id?: string;118mode?: "project" | "flyout";119}120121function OneSSHKey({ ssh_key, project_id, mode = "project" }: OneSSHKeyProps) {122const isFlyout = mode === "flyout";123124function render_last_use(): JSX.Element {125const d = ssh_key.get("last_use_date");126if (d) {127return (128<span style={{ color: "#1e7e34" }}>129Last used <TimeAgo date={new Date(d)} />130</span>131);132} else {133return <span style={{ color: "#333" }}>Never used</span>;134}135}136137function delete_key(): void {138const fingerprint = ssh_key.get("fingerprint");139if (project_id) {140redux.getActions("projects").delete_ssh_key_from_project({141fingerprint,142project_id: project_id,143});144} else {145redux.getActions("account").delete_ssh_key(fingerprint);146}147}148149const key_style: React.CSSProperties = {150fontSize: isFlyout ? "42px" : "72px",151color: ssh_key.get("last_use_date") ? "#1e7e34" : "#888",152};153154return (155<div156style={{157display: "flex",158borderBottom: "1px solid #ccc",159padding: "15px",160}}161>162<div style={{ width: isFlyout ? "48px" : "100px", display: "flex" }}>163<Icon style={key_style} name="key" />164</div>165<div style={{ flex: 1 }}>166<Popconfirm167title={168<div>169Are you sure you want to delete this SSH key? <br />170This CANNOT be undone. <br /> If you want to reuse this key in the171future, you will have to upload it again.172</div>173}174onConfirm={() => delete_key()}175okText={"Yes, delete key"}176cancelText={<CancelText />}177>178<Button179size={isFlyout ? "small" : "middle"}180style={{ float: "right" }}181>182<Icon name="trash" /> Delete...183</Button>184</Popconfirm>185<div style={{ fontWeight: 600 }}>{ssh_key.get("title")}</div>186<span style={{ fontWeight: 600 }}>Fingerprint: </span>187<Typography.Text code style={{ fontSize: "80%" }}>188{ssh_key.get("fingerprint")}189</Typography.Text>190<br />191Added on {new Date(ssh_key.get("creation_date")).toLocaleDateString()}192<div> {render_last_use()} (NOTE: not all usage is tracked.)</div>193</div>194</div>195);196}197198199