Skip to content

Commit

Permalink
feat: #263 store and display public ssh key
Browse files Browse the repository at this point in the history
  • Loading branch information
bohdan-shulha committed Nov 9, 2024
1 parent 342db69 commit 16070c2
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 123 deletions.
1 change: 1 addition & 0 deletions api-nodes/Http/Controllers/EventController.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public function started(Node $node, AgentStartedEventData $data)
$swarm->data->joinTokens = $data->swarm->joinTokens;
$swarm->data->managerNodes = $data->swarm->managerNodes;
$swarm->data->encryptionKey = $data->swarm->encryptionKey;
$swarm->data->publicSSHKey = $data->swarm->publicSSHKey;

$nodeAddresses = $swarm->nodes->pluck('data.address')->toArray();
$dockerServices = collect($swarm->services)
Expand Down
1 change: 1 addition & 0 deletions api-nodes/Models/AgentStartedEventData/SwarmData.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ public function __construct(
/* @var ManagerNode[] */
public array $managerNodes,
public string $encryptionKey,
public string $publicSSHKey,
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

use App\Models\DeploymentData\Volume;
use Spatie\LaravelData\Attributes\Validation\DoesntStartWith;
use Spatie\LaravelData\Attributes\Validation\StartsWith;
use Spatie\LaravelData\Data;

class GitWithDockerfileSource extends Data
{
public function __construct(
#[StartsWith('git@')]
public string $repo,
public string $ref,
#[DoesntStartWith('/')]
Expand Down
2 changes: 2 additions & 0 deletions app/Models/DeploymentData/AppSource/GitWithNixpacksSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
namespace App\Models\DeploymentData\AppSource;

use App\Models\DeploymentData\Volume;
use Spatie\LaravelData\Attributes\Validation\StartsWith;
use Spatie\LaravelData\Data;

class GitWithNixpacksSource extends Data
{
public function __construct(
#[StartsWith('git@')]
public string $repo,
public string $ref,
public string $nixpacksFilePath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ public function __construct(

public function formattedHtml(): string
{
return implode("<br>\n", $this->output);
return implode('<br>', $this->output);
}
}
1 change: 1 addition & 0 deletions app/Models/SwarmData.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public function __construct(
/* @var ManagerNode[] */
public array $managerNodes,
public string $encryptionKey = '',
public string $publicSSHKey = '',
) {}

public function findRegistry(string $id): ?DockerRegistry
Expand Down
104 changes: 104 additions & 0 deletions resources/js/Components/CopyToClipboard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<script setup>
import { onMounted, ref } from "vue";
import { CopyClipboard } from "flowbite";
const props = defineProps({
value: {
type: String,
required: true,
},
});
const trigger = ref(null);
const copyToClipboardRef = ref(null);
const showSuccessMessage = ref(false);
onMounted(() => {
const clipboard = new CopyClipboard(
trigger.value,
copyToClipboardRef.value,
);
clipboard.updateOnCopyCallback(() => {
showSuccessMessage.value = true;
setTimeout(() => {
showSuccessMessage.value = false;
}, 2000);
});
});
</script>

<template>
<div class="relative">
<div class="relative">
<div
v-if="$slots.icon"
class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none"
>
<slot name="icon"></slot>
</div>
<input
ref="copyToClipboardRef"
type="text"
:class="[
'p-2.5 bg-gray-50 border border-gray-300 text-gray-500 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full px-2.5 py-4 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500',
$slots.icon ? 'ps-10' : '',
]"
:value="value"
disabled
readonly
/>
</div>
<button
ref="trigger"
class="absolute end-2.5 w-24 top-1/2 -translate-y-1/2 text-gray-900 dark:text-gray-400 hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-600 dark:hover:bg-gray-700 rounded-lg py-2 px-2.5 inline-flex items-center justify-center bg-white border-gray-200 border"
>
<span v-auto-animate>
<span
v-if="!showSuccessMessage"
id="default-message"
class="inline-flex items-center"
>
<svg
class="w-3 h-3 me-1.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 18 20"
>
<path
d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z"
/>
</svg>
<span class="text-xs font-semibold">Copy</span>
</span>

<span
v-if="showSuccessMessage"
id="success-message"
class="inline-flex whitespace-nowrap items-center"
>
<svg
class="w-3 h-3 text-blue-700 dark:text-blue-500 me-1.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 16 12"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 5.917 5.724 10.5 15 1.5"
/>
</svg>
<span
class="text-xs font-semibold text-blue-700 dark:text-blue-500"
>Copied</span
>
</span>
</span>
</button>
</div>
</template>
145 changes: 28 additions & 117 deletions resources/js/Pages/Nodes/Partials/AgentInstall.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@ import { onMounted, ref } from "vue";
import { CopyClipboard } from "flowbite";
import InputLabel from "@/Components/InputLabel.vue";
import Tour from "@/Components/Tour.vue";
import CopyToClipboard from "@/Components/CopyToClipboard.vue";
defineProps({
node: Object,
});
const trigger = ref(null);
const copyToClipboardRef = ref(null);
const showSuccessMessage = ref(false);
const providers = [
{
name: "Hetzner",
Expand Down Expand Up @@ -46,30 +42,6 @@ const providers = [
},
];
onMounted(() => {
const clipboard = new CopyClipboard(
trigger.value,
copyToClipboardRef.value,
);
clipboard.updateOnCopyCallback((clipboard) => {
showSuccess();
// reset to default state
setTimeout(() => {
resetToDefault();
}, 2000);
});
const showSuccess = () => {
showSuccessMessage.value = true;
};
const resetToDefault = () => {
showSuccessMessage.value = false;
};
});
const steps = [
{
title: "Welcome to Ptah.sh!",
Expand Down Expand Up @@ -221,95 +193,34 @@ const steps = [
<InputLabel for="agent-install-cli">
Execute this script in your server's console to install an Agent
</InputLabel>
<div class="relative">
<div class="relative">
<div
class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none"
<CopyToClipboard
:value="
'export PTAH_TOKEN=' +
$props.node.agent_token +
' && curl -sSL https://r.ptah.sh/install-agent > /tmp/install.sh && bash /tmp/install.sh'
"
id="agent-install-cli"
>
<template #icon>
<svg
class="w-4 h-4 text-gray-800 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
>
<svg
class="w-4 h-4 text-gray-800 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 17.345a4.76 4.76 0 0 0 2.558 1.618c2.274.589 4.512-.446 4.999-2.31.487-1.866-1.273-3.9-3.546-4.49-2.273-.59-4.034-2.623-3.547-4.488.486-1.865 2.724-2.899 4.998-2.31.982.236 1.87.793 2.538 1.592m-3.879 12.171V21m0-18v2.2"
/>
</svg>
</div>
<input
id="agent-install-cli"
ref="copyToClipboardRef"
type="text"
class="ps-10 p-2.5 col-span-6 bg-gray-50 border border-gray-300 text-gray-500 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full px-2.5 py-4 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500"
:value="
'export PTAH_TOKEN=' +
$props.node.agent_token +
' && curl -sSL https://r.ptah.sh/install-agent > /tmp/install.sh && bash /tmp/install.sh'
"
disabled
readonly
/>
</div>
<button
ref="trigger"
class="absolute end-2.5 w-24 top-1/2 -translate-y-1/2 text-gray-900 dark:text-gray-400 hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-600 dark:hover:bg-gray-700 rounded-lg py-2 px-2.5 inline-flex items-center justify-center bg-white border-gray-200 border"
>
<span v-auto-animate>
<span
v-if="!showSuccessMessage"
id="default-message"
class="inline-flex items-center"
>
<svg
class="w-3 h-3 me-1.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 18 20"
>
<path
d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z"
/>
</svg>
<span class="text-xs font-semibold">Copy</span>
</span>

<span
v-if="showSuccessMessage"
id="success-message"
class="inline-flex whitespace-nowrap items-center"
>
<svg
class="w-3 h-3 text-blue-700 dark:text-blue-500 me-1.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 16 12"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 5.917 5.724 10.5 15 1.5"
/>
</svg>
<span
class="text-xs font-semibold text-blue-700 dark:text-blue-500"
>Copied</span
>
</span>
</span>
</button>
</div>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 17.345a4.76 4.76 0 0 0 2.558 1.618c2.274.589 4.512-.446 4.999-2.31.487-1.866-1.273-3.9-3.546-4.49-2.273-.59-4.034-2.623-3.547-4.488.486-1.865 2.724-2.899 4.998-2.31.982.236 1.87.793 2.538 1.592m-3.879 12.171V21m0-18v2.2"
/>
</svg>
</template>
</CopyToClipboard>
</div>
</div>
</template>
31 changes: 31 additions & 0 deletions resources/js/Pages/Nodes/Partials/PublicSSHKey.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script setup>
import ActionSection from "@/Components/ActionSection.vue";
import CopyToClipboard from "@/Components/CopyToClipboard.vue";
import ExternalLink from "@/Components/ExternalLink.vue";
defineProps({
swarm: Object,
});
</script>

<template>
<ActionSection>
<template #title> Public SSH Key </template>

<template #description>
Use this SSH Key as a deploy key in your Git repository.

<ExternalLink
href="https://ptah.sh/concepts/workers/#git-based-deployments/"
>
Learn about Git based deployments
</ExternalLink>
</template>

<template #content>
<div class="col-span-full">
<CopyToClipboard :value="swarm.data.publicSSHKey" />
</div>
</template>
</ActionSection>
</template>
6 changes: 6 additions & 0 deletions resources/js/Pages/Nodes/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import DockerRegistries from "@/Pages/Nodes/Partials/DockerRegistries.vue";
import S3Storages from "@/Pages/Nodes/Partials/S3Storages.vue";
import DeleteResourceSection from "@/Components/DeleteResourceSection.vue";
import { router } from "@inertiajs/vue3";
import PublicSSHKey from "./Partials/PublicSSHKey.vue";
const props = defineProps([
"node",
Expand Down Expand Up @@ -58,9 +59,14 @@ const destroyNode = () => router.delete(route("nodes.destroy", props.node.id));
v-if="$props.initTaskGroup"
:taskGroup="$props.initTaskGroup"
/>

<template
v-if="!$props.initTaskGroup && $props.node.swarm_id !== null"
>
<PublicSSHKey :swarm="$props.node.swarm" />

<SectionBorder />

<S3Storages
:swarm="$props.node.swarm"
:task-group="$props.s3SUpdateTaskGroup"
Expand Down
Loading

0 comments on commit 16070c2

Please sign in to comment.