Skip to content

Commit

Permalink
Change: ssh keys in the UI (#279)
Browse files Browse the repository at this point in the history
* ssh keys

* change mutation
  • Loading branch information
DaveDarsa authored Jul 4, 2024
1 parent 1531166 commit f7c881b
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 80 deletions.
45 changes: 18 additions & 27 deletions src/components/SshKeys/AddSshKey.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useState } from 'react';
import { Mutation } from 'react-apollo';

import { Space } from 'antd';
import Button from 'components/Button';
import Me from 'lib/query/Me';

import AddSshKeyMutation from '../../lib/mutation/AddSshKey';
import AddUserSSHPublicKey from '../../lib/mutation/AddUserSSHPublicKey';

const AddSshKey = ({ me: { id, email } }) => {
const defaultValues = { sshKeyName: '', sshKey: '' };
Expand All @@ -15,42 +16,29 @@ const AddSshKey = ({ me: { id, email } }) => {
setValues({ ...values, [name]: value });
};

const regex = /\s*(ssh-rsa|ssh-ed25519|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521)\s(\S+)/;
// First capture group is the type of the ssh key
// Second capture group is the actual ssh key
// Whitespace and comments are ignored

const isFormValid = values.sshKeyName !== '' && !values.sshKey.includes('\n') && values.sshKey.match(regex);

return (
<div className="addSshKey">
<Mutation mutation={AddSshKeyMutation} refetchQueries={[{ query: Me }]}>
{(addSshKey, { loading, called, error, data }) => {
<Mutation mutation={AddUserSSHPublicKey} refetchQueries={[{ query: Me }]} onError={e => console.error(e)}>
{(addUserSSHPublicKey, { loading, called, error, data }) => {
const addSshKeyHandler = () => {
addSshKey({
addUserSSHPublicKey({
variables: {
input: {
name: values.sshKeyName,
keyValue: values.sshKey.match(regex)[2],
keyType: values.sshKey.match(regex)[1].replace(/-/g, '_').toUpperCase(),
publicKey: values.sshKey,
user: {
id,
email,
},
},
},
});

setValues(defaultValues);
};

if (!error && called && loading) {
return <div>Adding SSH Key...</div>;
}

return (
<div className="addNew">
{error ? <div className="error">{error.message.replace('GraphQL error:', '').trim()}</div> : ''}

<div>
<label htmlFor="sshKeyName">SSH Key Name</label>
<input
Expand All @@ -77,14 +65,17 @@ const AddSshKey = ({ me: { id, email } }) => {
placeholder="Begins with 'ssh-rsa', 'ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521'"
/>
</div>
<Button disabled={!isFormValid} action={addSshKeyHandler}>
Add
</Button>
<div className="sshKeyError">
<span className={values.sshKey == '' || isFormValid ? 'fade' : 'fade-in'}>
{values.sshKeyName == '' ? 'Please enter a SSH Key name' : 'The SSH Key entered is invalid'}
</span>
</div>
<Space direction="vertical" size="large">
<Button
loading={!error && called && loading}
disabled={!values.sshKey || !values.sshKeyName}
action={addSshKeyHandler}
>
Add
</Button>

{error ? <div className="error">{error.message.replace('GraphQL error:', '').trim()}</div> : ''}
</Space>
</div>
);
}}
Expand Down
173 changes: 142 additions & 31 deletions src/components/SshKeys/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,36 @@ import React, { useEffect, useState } from 'react';
import { Mutation } from 'react-apollo';
import Skeleton from 'react-loading-skeleton';

import { Col, Modal, Row, Space } from 'antd';
import Button from 'components/Button';
import DeleteSshKeyById from 'lib/mutation/DeleteSshKeyById';
import DeleteUserSSHPublicKey from 'lib/mutation/DeleteUserSSHPublicKey';
import UpdateUserSSHPublicKey from 'lib/mutation/UpdateUserSSHPublicKey';
import Me from 'lib/query/Me';
import moment from 'moment';

import { StyledKeys } from './StyledKeys';

const SshKeys = ({ me: { id, email, sshKeys: keys }, loading }) => {
const SshKeys = ({ me: { id, email, sshKeys: keys }, loading, handleRefetch }) => {
const [isLoading, setIsLoading] = useState(loading);

const [modalOpen, setModalOpen] = useState(false);

const [editState, setEditState] = useState({
id: '',
name: '',
publicKey: '',
});

const closeModal = () => {
setEditState({ name: '', publicKey: '' });
setModalOpen(false);
};

const openModal = keyObject => {
setEditState(keyObject);
setModalOpen(true);
};

useEffect(() => {
setIsLoading(loading);
}, [loading]);
Expand Down Expand Up @@ -41,36 +61,127 @@ const SshKeys = ({ me: { id, email, sshKeys: keys }, loading }) => {
<div className="created chromatic-ignore">
{moment.utc(key.created).local().format('DD MMM YYYY, HH:mm:ss (Z)')}
</div>
<div className="delete" data-cy="deleteKey">
<Mutation mutation={DeleteSshKeyById} refetchQueries={[{ query: Me }]}>
{(deleteSshKeyById, { loading, called, error, data }) => {
if (error) {
return <div>{error.message}</div>;
}

if (called) {
return <div>Deleting SSH Key...</div>;
}
return (
<Button
testId="deleteBtn"
variant="red"
action={() =>
deleteSshKeyById({
variables: {
input: {
id: key.id,

<Space>
<div className="edit" data-cy="editKey">
<Button testId="editBtn" action={() => openModal(key)}>
Edit
</Button>
<Modal
open={modalOpen && editState.id === key.id}
title="Edit SSH Key"
onCancel={closeModal}
destroyOnClose
className="sshmodal"
footer={() => (
<>
<Space>
<Mutation mutation={UpdateUserSSHPublicKey} onError={e => console.error(e)}>
{(updateUserSSHPublicKey, { loading, called, error, data }) => {
if (error) {
return <div>{error.message}</div>;
}

if (data) {
handleRefetch();
closeModal();
}

return (
<Button
loading={called || loading}
testId="updateKey"
action={() =>
updateUserSSHPublicKey({
variables: {
input: {
id: key.id,
patch: {
name: editState.name,
publicKey: editState.publicKey,
},
},
},
})
}
>
Update
</Button>
);
}}
</Mutation>

<Button variant="white" testId="cancelBtn" action={closeModal}>
Cancel
</Button>
</Space>
</>
)}
>
<Row>
<Col span={24}>SSH KEY NAME:</Col>

<Col span={24}>
<input
className="inputKeyName fullSize"
type="text"
value={editState.name}
onChange={e => {
setEditState({ ...editState, name: e.target.value });
}}
/>
</Col>
</Row>

<Row>
<Col span={24}>SSH KEY: </Col>
<Col span={24}>
<input
className="inputKey fullSize"
type="text"
value={editState.publicKey}
onChange={e => {
setEditState({ ...editState, publicKey: e.target.value });
}}
/>
</Col>
</Row>
</Modal>
</div>

<div className="delete" data-cy="deleteKey">
<Mutation
mutation={DeleteUserSSHPublicKey}
refetchQueries={[{ query: Me }]}
onError={e => console.error(e)}
>
{(deleteUserSSHPublicKey, { loading, called, error, data }) => {
if (error) {
return <div>{error.message}</div>;
}

return (
<Button
testId="deleteBtn"
loading={called}
variant="red"
action={() =>
deleteUserSSHPublicKey({
variables: {
input: {
id: key.id,
},
},
},
})
}
>
Delete
</Button>
);
}}
</Mutation>
</div>
})
}
>
Delete
</Button>
);
}}
</Mutation>
</div>
</Space>
</div>
))}
</div>
Expand Down
18 changes: 18 additions & 0 deletions src/layouts/GlobalStyles/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,24 @@ body {
background: ${props => (props.theme.colorScheme === 'dark' ? '#fff' : '#000')};
}
}
.ant-modal .ant-modal-content{
background: ${props => (props.theme.colorScheme === 'dark' ? '#eaeaea' : '#fff')};
.ant-modal-header{
border-radius: 0;
background-color: transparent;
}
.ant-modal-body {
margin-block: 1rem;
> * {
margin-bottom: 0.5rem;
}
}
input.fullSize{
width: 100%;
padding: 0.25rem 0.5rem;
}
}
#__next {
display: flex;
Expand Down
12 changes: 0 additions & 12 deletions src/lib/mutation/AddSshKey.js

This file was deleted.

12 changes: 12 additions & 0 deletions src/lib/mutation/AddUserSSHPublicKey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import gql from 'graphql-tag';

export default gql`
mutation addUserSSHPublicKey($input: AddUserSSHPublicKeyInput!) {
addUserSSHPublicKey(input: $input) {
id
name
keyValue
created
}
}
`;
7 changes: 0 additions & 7 deletions src/lib/mutation/DeleteSshKeyById.js

This file was deleted.

7 changes: 7 additions & 0 deletions src/lib/mutation/DeleteUserSSHPublicKey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import gql from 'graphql-tag';

export default gql`
mutation deleteUserSSHPublicKey($input: DeleteUserSSHPublicKeyByIdInput!) {
deleteUserSSHPublicKey(input: $input)
}
`;
11 changes: 11 additions & 0 deletions src/lib/mutation/UpdateUserSSHPublicKey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import gql from 'graphql-tag';

export default gql`
mutation updateUserSSHPublicKey($input: UpdateUserSSHPublicKeyInput!) {
updateUserSSHPublicKey(input: $input) {
id
name
keyValue
}
}
`;
9 changes: 6 additions & 3 deletions src/pages/settings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ import { CommonWrapper } from '../../styles/commonPageStyles';
* Displays the user settings page.
*/
const SettingsPage = () => {
const { data, loading, error } = useQuery(Me, {
const queryVars = {
displayName: 'Me',
fetchPolicy: 'cache-and-network',
});
};
const { data, loading, error, refetch } = useQuery(Me, queryVars);

const handleRefetch = async () => await refetch(queryVars);

if (error) {
return <QueryError error={error} />;
Expand All @@ -33,7 +36,7 @@ const SettingsPage = () => {
<CommonWrapper>
<h2>SSH keys</h2>
<div className="content">
<SshKeys me={data?.me || {}} loading={loading} />
<SshKeys me={data?.me || {}} loading={loading} handleRefetch={handleRefetch} />
<AddSshKey me={data?.me || {}} />
</div>
</CommonWrapper>
Expand Down

0 comments on commit f7c881b

Please sign in to comment.