119 lines
3.6 KiB
Svelte
119 lines
3.6 KiB
Svelte
<script>
|
|
import { createEventDispatcher } from 'svelte';
|
|
import { invoke } from '@tauri-apps/api/core';
|
|
import { homeDir } from '@tauri-apps/api/path';
|
|
import { fade } from 'svelte/transition';
|
|
|
|
import ErrorAlert from '../../ui/ErrorAlert.svelte';
|
|
import FileInput from '../../ui/FileInput.svelte';
|
|
import PassphraseInput from '../../ui/PassphraseInput.svelte';
|
|
import Spinner from '../../ui/Spinner.svelte';
|
|
|
|
export let record;
|
|
|
|
let name;
|
|
let file;
|
|
let privateKey = '';
|
|
let passphrase = '';
|
|
let showDetails = true;
|
|
let mode = 'file';
|
|
|
|
const dispatch = createEventDispatcher();
|
|
|
|
let defaultPath = null;
|
|
homeDir().then(d => defaultPath = `${d}/.ssh`);
|
|
|
|
|
|
let alert;
|
|
let saving = false;
|
|
async function saveCredential() {
|
|
saving = true;
|
|
try {
|
|
let key = await getKey();
|
|
const payload = {
|
|
id: record.id,
|
|
name,
|
|
is_default: false, // ssh keys don't care about defaults
|
|
credential: {type: 'Ssh', ...key},
|
|
};
|
|
await invoke('save_credential', {record: payload});
|
|
dispatch('save', payload);
|
|
}
|
|
finally {
|
|
saving = false;
|
|
}
|
|
}
|
|
|
|
async function getKey() {
|
|
if (mode === 'file') {
|
|
return await invoke('sshkey_from_file', {path: file.path, passphrase});
|
|
}
|
|
else {
|
|
return await invoke('sshkey_from_private_key', {privateKey, passphrase});
|
|
}
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
<div role="tablist" class="join max-w-sm mx-auto flex justify-center">
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
class="join-item flex-1 btn border border-primary hover:border-primary"
|
|
class:btn-primary={mode === 'file'}
|
|
on:click={() => mode = 'file'}
|
|
>
|
|
From file
|
|
</button>
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
class="join-item flex-1 btn border border-primary hover:border-primary"
|
|
class:btn-primary={mode === 'direct'}
|
|
on:click={() => mode = 'direct'}
|
|
>
|
|
From private key
|
|
</button>
|
|
</div>
|
|
|
|
|
|
<form class="space-y-4" on:submit|preventDefault={alert.run(saveCredential)}>
|
|
<ErrorAlert bind:this={alert} />
|
|
|
|
<div class="grid grid-cols-[auto_1fr] items-center gap-4">
|
|
<span class="justify-self-end">Name</span>
|
|
<input
|
|
type="text"
|
|
class="input input-bordered bg-transparent"
|
|
bind:value={name}
|
|
>
|
|
|
|
{#if mode === 'file'}
|
|
<span class="justify-self-end">File</span>
|
|
<FileInput params={{defaultPath}} bind:value={file} on:update={() => name = file.name} />
|
|
{:else}
|
|
<span class="justify-self-end">Private key</span>
|
|
<textarea bind:value={privateKey} rows="5" class="textarea textarea-bordered bg-transparent font-mono whitespace-pre overflow-x-auto"></textarea>
|
|
{/if}
|
|
|
|
<span class="justify-self-end">Passphrase</span>
|
|
<PassphraseInput class="bg-transparent" bind:value={passphrase} />
|
|
</div>
|
|
|
|
<div class="flex justify-end">
|
|
{#if file?.path || privateKey !== ''}
|
|
<button
|
|
transition:fade={{duration: 100}}
|
|
type="submit"
|
|
class="btn btn-primary"
|
|
>
|
|
{#if saving}
|
|
<Spinner class="size-5 min-w-16" thickness="12" />
|
|
{:else}
|
|
<span class="min-w-16">Save</span>
|
|
{/if}
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
</form> |