Skip to content

Commit

Permalink
Enhanced the 'SSH-Keys' page with the following:
Browse files Browse the repository at this point in the history
- The validation rule was modified to accept keys without names

- Take the key name when importing an 'SSH key' if exists or generate a new one

- Migrating the old ssh key after activating the 'profile manager'

- Created a class named 'SSHKeyData' that holds all 'SSH' functionality, to avoid code duplication since there are some functions that need to be imported into other places

- Disable the 'Import', 'Generate', and 'Export' buttons while 'Deleteing', 'Creating', and 'Updating' keys to avoid errors

- Wait on the chain to finish the task then update the table of 'ssh keys'
  • Loading branch information
Mahmoud-Emad committed Apr 18, 2024
1 parent 8957552 commit 9ce6514
Show file tree
Hide file tree
Showing 8 changed files with 413 additions and 371 deletions.
48 changes: 32 additions & 16 deletions packages/playground/src/components/ssh_keys/SshFormDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
<template v-slot:default>
<v-card>
<v-toolbar color="primary" class="custom-toolbar">
<p v-if="$props.dialogType === SSHCreationMethod.Generate" class="mb-5">Add new SSH Key</p>
<p v-else-if="$props.dialogType === SSHCreationMethod.Import" class="mb-5">Import an exact SSH Key</p>
<p class="mb-5">
{{
$props.dialogType === SSHCreationMethod.Generate ? "Generate a new SSH Key" : "Import an exact SSH Key"
}}
</p>
</v-toolbar>

<v-card-text>
Expand All @@ -21,7 +24,7 @@
<v-text-field
hint="Leave this field empty to generate a name automatically, or enter a custom name to save it with your key."
:rules="[
keyName.length < 15 || 'Please enter a key name with fewer than 15 characters.',
keyName.length < 30 || 'Please enter a key name with fewer than 30 characters.',
!keyName.includes(' ') || 'Key names cannot include spaces. Please use a name without spaces.',
]"
class="mb-4"
Expand All @@ -32,7 +35,8 @@
</input-tooltip>

<v-alert width="95%" class="mb-4" type="info">
Updating or generating SSH key will cost you up to 0.01 TFT
{{ $props.dialogType === SSHCreationMethod.Generate ? "Generating" : "Importing" }}
a new SSH key will cost you up to 0.01 TFT
</v-alert>

<div v-if="$props.dialogType === SSHCreationMethod.Generate" class="create">
Expand Down Expand Up @@ -82,7 +86,7 @@
<v-btn
v-if="$props.dialogType === SSHCreationMethod.Import"
:loading="$props.savingKey"
:disabled="!sshKey || !isValidSSHKey(sshKey)"
:disabled="!sshKey || !sshKeysManagement.isValidSSHKey(sshKey) || keyName.length >= 30"
color="secondary"
variant="outlined"
text="Save"
Expand All @@ -100,11 +104,10 @@ import { computed, defineComponent, type PropType, ref, watch } from "vue";
import { type Profile, useProfileManager } from "@/stores/profile_manager";
import { SSHCreationMethod, type SSHKeyData } from "@/types";
import { formatSSHKeyTableCreatedAt } from "@/utils/date";
import { createCustomToast, ToastType } from "@/utils/custom_toast";
import { type Balance, getGrid, loadBalance } from "@/utils/grid";
import { isEnoughBalance } from "@/utils/helpers";
import { generateSSHKeyName } from "@/utils/strings";
import { isValidSSHKey } from "@/utils/validators";
import SSHKeysManagement from "@/utils/ssh";
export default defineComponent({
emits: ["close", "save", "generate"],
Expand Down Expand Up @@ -137,6 +140,8 @@ export default defineComponent({
setup(props, { emit }) {
const profileManager = useProfileManager();
const sshKeysManagement = new SSHKeysManagement();
const sshKey = ref<string>("");
const keyName = ref<string>(generateUniqueSSHKeyName());
const createdKey = ref<SSHKeyData | null>(null); // Initialize createdKey with null
Expand All @@ -150,17 +155,18 @@ export default defineComponent({
isOpen => {
if (isOpen) {
const now = new Date();
const lastID = props.allKeys.length ? props.allKeys[props.allKeys.length - 1].id : 1;
const keyId = props.allKeys.length ? props.allKeys[props.allKeys.length - 1].id + 1 : 1;
keyName.value = generateUniqueSSHKeyName();
sshKey.value = "";
createdKey.value = {
id: lastID + 1,
id: keyId,
publicKey: props.generatedSshKey as string,
createdAt: formatSSHKeyTableCreatedAt(now),
name: keyName.value.length === 0 ? generateUniqueSSHKeyName() : keyName.value,
createdAt: sshKeysManagement.formatDate(now),
name: keyName.value,
isActive: true,
};
} else {
keyName.value = generateUniqueSSHKeyName();
}
},
{ deep: true },
Expand All @@ -184,12 +190,22 @@ export default defineComponent({
if (createdKey.value) {
const isNewSSHKey = ref<boolean>(props.dialogType === SSHCreationMethod.Generate);
createdKey.value.publicKey = isNewSSHKey.value ? props.generatedSshKey || "" : sshKey.value;
const parts = createdKey.value.publicKey.split(" ");
if (parts.length === 3) {
if (parts[parts.length - 1].length < 30) {
keyName.value = parts[parts.length - 1];
}
}
createdKey.value.name = keyName.value;
emit("save", createdKey.value);
}
}
function generateUniqueSSHKeyName(depth = 0): string {
const keyName: string = generateSSHKeyName();
const keyName: string = sshKeysManagement.generateName();
if (!props.allKeys.length) {
return keyName;
}
Expand Down Expand Up @@ -241,7 +257,7 @@ export default defineComponent({
return [
(v: any) => !!v || "SSH key is required.",
(v: string) =>
isValidSSHKey(v) ||
sshKeysManagement.isValidSSHKey(v) ||
"The SSH key you provided is not valid. Please double-check that it is copied correctly and follows the correct format.",
];
}
Expand All @@ -252,12 +268,12 @@ export default defineComponent({
hasEnoughBalance,
sshKey,
SSHCreationMethod,
sshKeysManagement,
generateUniqueSSHKeyName,
createNewSSHKey,
sshRules,
generateSSHKey,
isValidSSHKey,
};
},
});
Expand Down
79 changes: 35 additions & 44 deletions packages/playground/src/components/ssh_keys/SshTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<v-card class="pt-6 pl-6 pr-6 mb-4">
<div class="head">
<h3 class="text-light">
<v-icon>{{ headerIcon }}</v-icon>
{{ headerTitle }}
<v-icon>mdi-key-chain</v-icon>
SSH Keys
</h3>
</div>

Expand All @@ -24,7 +24,11 @@
loading-text="Loading..."
@click:row="(_, { item }) => $emit('view', item.raw)"
>
<template #loading> </template>
<template #loading>
<div class="w-100 text-center" v-if="loading && loadingMessage">
<small>{{ loadingMessage }}</small>
</div>
</template>
<template #[`item.createdAt`]="{ item }">
<v-tooltip location="bottom" :text="`The date when this SSH key was created.`">
<template #activator="{ props }">
Expand Down Expand Up @@ -64,8 +68,11 @@
<template #[`item.activation`]="{ item }">
<v-tooltip
location="bottom"
v-if="item.raw.isActive"
:text="`The '${item.raw.name}' key is currently activated and will be utilized in deployments.`"
:text="
item.raw.isActive
? `The '${item.raw.name}' key is currently activated and will be utilized in deployments.`
: `Click to activate the '${item.raw.name}' for use in deployments.`
"
>
<template #activator="{ props }">
<v-progress-circular
Expand All @@ -80,32 +87,9 @@
class="d-inline"
v-bind="props"
color="secondary"
@click.stop="activateKey(item.raw)"
:model-value="item.raw.isActive"
/>
</template>
</v-tooltip>

<v-tooltip
location="bottom"
v-else
:text="`Click to activate the '${item.raw.name}' for use in deployments.`"
>
<template #activator="{ props }">
<v-progress-circular
v-if="item.raw.activating"
:size="20"
:width="2"
color="info"
indeterminate
></v-progress-circular>
<VCheckboxBtn
v-else
class="d-inline"
v-bind="props"
:color="theme.name.value === AppThemeSelection.light ? '' : 'grey-lighten-1'"
@click.stop="activateKey(item.raw)"
@click.stop="toggleKeyActivation(item.raw)"
:model-value="item.raw.isActive"
:disabled="deleting"
/>
</template>
</v-tooltip>
Expand Down Expand Up @@ -165,11 +149,7 @@ export default defineComponent({
type: Array as PropType<SSHKeyData[]>,
required: true,
},
headerTitle: {
type: String,
required: true,
},
headerIcon: {
loadingMessage: {
type: String,
required: true,
},
Expand All @@ -183,7 +163,7 @@ export default defineComponent({
},
},
emits: ["inactive", "active", "delete", "view", "update:keys", "export"],
emits: ["delete", "view", "update:activation", "export"],
setup(props, { emit }) {
const selectedKeys = ref<number[]>([]); // IDs
Expand Down Expand Up @@ -226,20 +206,26 @@ export default defineComponent({
emit("delete", [key]);
};
const activateKey = (key: SSHKeyData) => {
if (key.isActive) {
emit("inactive", key);
} else {
emit("active", key);
}
const toggleKeyActivation = (key: SSHKeyData) => {
console.log(key.isActive);
emit("update:activation", key);
};
return { headers, selectedKeys, capitalize, theme, AppThemeSelection, deleteSelected, deleteKey, activateKey };
return {
headers,
selectedKeys,
theme,
AppThemeSelection,
capitalize,
deleteSelected,
deleteKey,
toggleKeyActivation,
};
},
});
</script>

<style scoped>
<style>
.head {
border-bottom: 1px solid #8d848d;
padding-bottom: 15px;
Expand All @@ -253,4 +239,9 @@ export default defineComponent({
.activation {
cursor: pointer;
}
.v-data-table-rows-loading td {
padding: 0 !important;
margin: 0 !important;
height: 25px !important;
}
</style>
4 changes: 0 additions & 4 deletions packages/playground/src/utils/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,3 @@ import moment from "moment";
export default function toHumanDate(timeInSeconds: number): string {
return moment(timeInSeconds * 1000).format("M/D/YY, h:m A");
}

export function formatSSHKeyTableCreatedAt(date: Date) {
return `${date.getDate()}-${date.getMonth() + 1}-${date.getFullYear()}`;
}
Loading

0 comments on commit 9ce6514

Please sign in to comment.