feat: add new settings view for Keys

Currently the SSH and GPG keys are hardcoded. In a refactoring those
will be passed down as prop to the component. But I would need to query
Vervis to receive the real keys. Likewise I am not talking to Vervis
when updating (remove, add) a key.

Signed-off-by: André Jaenisch <andre.jaenisch@posteo.de>
This commit is contained in:
André Jaenisch 2024-08-06 09:33:46 +02:00
parent 03c8334338
commit 55602e359a
No known key found for this signature in database
GPG key ID: 5A668E771F1ED854
13 changed files with 415 additions and 9 deletions

10
package-lock.json generated
View file

@ -1,14 +1,15 @@
{ {
"name": "anvil", "name": "anvil",
"version": "0.0.11", "version": "0.0.12",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "anvil", "name": "anvil",
"version": "0.0.11", "version": "0.0.12",
"dependencies": { "dependencies": {
"@floating-ui/dom": "1.6.8", "@floating-ui/dom": "1.6.8",
"@fontsource/spline-sans-mono": "5.0.20",
"axios": "1.7.2", "axios": "1.7.2",
"cheerio": "1.0.0-rc.12" "cheerio": "1.0.0-rc.12"
}, },
@ -2202,6 +2203,11 @@
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.5.tgz", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.5.tgz",
"integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ==" "integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ=="
}, },
"node_modules/@fontsource/spline-sans-mono": {
"version": "5.0.20",
"resolved": "https://registry.npmjs.org/@fontsource/spline-sans-mono/-/spline-sans-mono-5.0.20.tgz",
"integrity": "sha512-4u23nOkNK1B6rkmNfiodQaMu9CKtKzrK/lTEAhBuQT08cTm6544uTQSoH8ssylDbdyYZZAWyVA2Mua/TdIuoxQ=="
},
"node_modules/@formatjs/ecma402-abstract": { "node_modules/@formatjs/ecma402-abstract": {
"version": "1.18.2", "version": "1.18.2",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz",

View file

@ -1,6 +1,6 @@
{ {
"name": "anvil", "name": "anvil",
"version": "0.0.11", "version": "0.0.12",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
@ -75,6 +75,7 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@floating-ui/dom": "1.6.8", "@floating-ui/dom": "1.6.8",
"@fontsource/spline-sans-mono": "5.0.20",
"axios": "1.7.2", "axios": "1.7.2",
"cheerio": "1.0.0-rc.12" "cheerio": "1.0.0-rc.12"
} }

View file

@ -13,6 +13,7 @@ You should have received a copy of the GNU Affero General Public License along w
<script> <script>
import SettingsAccount from './SettingsAccount.svelte'; import SettingsAccount from './SettingsAccount.svelte';
import SettingsKeys from './SettingsKeys.svelte';
import SettingsSidebar from '../atoms/SettingsSidebar.svelte'; import SettingsSidebar from '../atoms/SettingsSidebar.svelte';
import SettingsProfile from './SettingsProfile.svelte'; import SettingsProfile from './SettingsProfile.svelte';
@ -30,5 +31,7 @@ You should have received a copy of the GNU Affero General Public License along w
<SettingsProfile /> <SettingsProfile />
{:else if activeSetting === 'account'} {:else if activeSetting === 'account'}
<SettingsAccount /> <SettingsAccount />
{:else if activeSetting === 'ssh_gpg_keys'}
<SettingsKeys />
{/if} {/if}
</div> </div>

View file

@ -0,0 +1,111 @@
<!--
SettingsKeys molecule.
Copyright (C) 2024 André Jaenisch
SPDX-FileCopyrightText: 2024 André Jaenisch
SPDX-License-Identifier: AGPL-3.0-or-later
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<script>
import '@fontsource/spline-sans-mono';
import { _ } from 'svelte-i18n';
import { Key24 } from 'svelte-octicons';
const gpg_keys = [
{
title: 'Work key',
fingerprint: 'CB9E C70F 2421 AF06 7D72 F980 8287 6A15 311B 1F84'
}
];
const ssh_keys = [
{
title: 'MyLaptop',
fingerprint: '25:c7:06:3f:87:49:5d:95:96:3d:a8'
},
{
title: 'Desktop',
fingerprint: '87:49:5d:95:96:3d:a8:25:c7:06:3f'
}
];
</script>
<div class="flex flex-col flex-1 gap-4 pe-8 pb-8 ps-8 pt-20 bg-surface-100">
<div class="flex text-surface-500 text-xl font-semibold">
{$_('settings.ssh_gpg_keys.headline')}
</div>
<div class="flex flex-col gap-6">
<div class="flex flex-col gap-1">
<div class="flex text-surface-500 font-semibold py-1">
{$_('settings.ssh_gpg_keys.ssh.headline')}
</div>
<div class="flex flex-col gap-2">
<!-- Nesting required to trigger last: class -->
{#each ssh_keys as key}
<div
class="flex border-b border-surface-200 last:border-b-0 items-center justify-between gap-4 pb-2"
>
<Key24 fill="currentColor" />
<span class="text-surface-500 flex-1 shrink-0">{key.title}</span>
<code class="text-surface-500 shrink-0">{key.fingerprint}</code>
<button type="button" class="btn font-semibold variant-filled-warning"
>{$_('settings.ssh_gpg_keys.ssh.remove')}</button
>
</div>
{/each}
</div>
<input
type="text"
class="input bg-white placeholder:text-surface-300"
placeholder={$_('settings.ssh_gpg_keys.ssh.title.placeholder')}
/>
<textarea
placeholder={$_('settings.ssh_gpg_keys.ssh.key.placeholder')}
rows="4"
class="placeholder:text-surface-300"
></textarea>
<button type="button" class="btn font-semibold text-white variant-filled-success self-end"
>{$_('settings.ssh_gpg_keys.ssh.add')}</button
>
</div>
<div class="flex flex-col gap-1">
<div class="flex text-surface-500 font-semibold py-1">
{$_('settings.ssh_gpg_keys.gpg.headline')}
</div>
<div class="flex flex-col gap-2">
<!-- Nesting required to trigger last: class -->
{#each gpg_keys as key}
<div
class="flex border-b border-surface-200 last:border-b-0 items-center justify-between gap-4 pb-2"
>
<Key24 fill="currentColor" />
<span class="text-surface-500 flex-1 shrink-0">{key.title}</span>
<code class="text-surface-500 font-medium shrink-0 w-[245px]">{key.fingerprint}</code>
<button type="button" class="btn font-semibold variant-filled-warning"
>{$_('settings.ssh_gpg_keys.gpg.remove')}</button
>
</div>
{/each}
</div>
<input
type="text"
class="input bg-white placeholder:text-surface-300"
placeholder={$_('settings.ssh_gpg_keys.gpg.title.placeholder')}
/>
<textarea
placeholder={$_('settings.ssh_gpg_keys.gpg.key.placeholder')}
rows="4"
class="placeholder:text-surface-300"
></textarea>
<button type="button" class="btn font-semibold text-white variant-filled-success self-end"
>{$_('settings.ssh_gpg_keys.gpg.add')}</button
>
</div>
</div>
</div>

View file

@ -228,7 +228,30 @@
} }
}, },
"ssh_gpg_keys": { "ssh_gpg_keys": {
"label": "" "gpg": {
"add": "",
"headline": "",
"key": {
"placeholder": ""
},
"remove": "",
"title": {
"placeholder": ""
}
},
"headline": "",
"label": "",
"ssh": {
"add": "",
"headline": "",
"key": {
"placeholder": ""
},
"remove": "",
"title": {
"placeholder": ""
}
}
} }
} }
} }

View file

@ -228,7 +228,30 @@
} }
}, },
"ssh_gpg_keys": { "ssh_gpg_keys": {
"label": "" "gpg": {
"add": "",
"headline": "",
"key": {
"placeholder": ""
},
"remove": "",
"title": {
"placeholder": ""
}
},
"headline": "",
"label": "",
"ssh": {
"add": "",
"headline": "",
"key": {
"placeholder": ""
},
"remove": "",
"title": {
"placeholder": ""
}
}
} }
} }
} }

View file

@ -228,7 +228,30 @@
} }
}, },
"ssh_gpg_keys": { "ssh_gpg_keys": {
"label": "SSH/GPG Keys" "gpg": {
"add": "Add GPG key",
"headline": "GPG",
"key": {
"placeholder": "Don't paste the private part of the GPG key. Paste the public part which begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'."
},
"remove": "Remove",
"title": {
"placeholder": "Title"
}
},
"headline": "SSH/GPG Keys",
"label": "SSH/GPG Keys",
"ssh": {
"add": "Add SSH key",
"headline": "SSH",
"key": {
"placeholder": "Begins with 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519', 'sk-ecdsa-sha2-nistp256@openssh.com', or 'sk-ssh-ed25519@openssh.com'"
},
"remove": "Remove",
"title": {
"placeholder": "Title"
}
}
} }
} }
} }

View file

@ -228,7 +228,30 @@
} }
}, },
"ssh_gpg_keys": { "ssh_gpg_keys": {
"label": "" "gpg": {
"add": "",
"headline": "",
"key": {
"placeholder": ""
},
"remove": "",
"title": {
"placeholder": ""
}
},
"headline": "",
"label": "",
"ssh": {
"add": "",
"headline": "",
"key": {
"placeholder": ""
},
"remove": "",
"title": {
"placeholder": ""
}
}
} }
} }
} }

View file

@ -228,7 +228,30 @@
} }
}, },
"ssh_gpg_keys": { "ssh_gpg_keys": {
"label": "" "gpg": {
"add": "",
"headline": "",
"key": {
"placeholder": ""
},
"remove": "",
"title": {
"placeholder": ""
}
},
"headline": "",
"label": "",
"ssh": {
"add": "",
"headline": "",
"key": {
"placeholder": ""
},
"remove": "",
"title": {
"placeholder": ""
}
}
} }
} }
} }

View file

@ -0,0 +1,36 @@
/*
* MIT License
*
* Copyright (c) 2020-present Eric Liu
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*/
/*
* SPDX-FileCopyrightText: 2020 - 2024 Eric Liu
* SPDX-License-Identifier: MIT
*/
import type { Meta, StoryObj } from '@storybook/svelte';
import { Key24 } from 'svelte-octicons';
const meta = {
title: 'Icons/Key24',
component: Key24,
tags: ['autodocs'],
argTypes: {
fill: {
control: 'color'
}
}
} satisfies Meta<Key24>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Plain: Story = {};

View file

@ -0,0 +1,26 @@
/* Stories for SettingsKeys molecule.
* Copyright (C) 2024 André Jaenisch
* SPDX-FileCopyrightText: 2024 André Jaenisch
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import type { Meta, StoryObj } from '@storybook/svelte';
import SettingsKeys from '$lib/components/molecules/SettingsKeys.svelte';
const meta = {
title: 'Molecules/SettingsKeys',
component: SettingsKeys,
tags: ['autodocs']
} satisfies Meta<SettingsKeys>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Plain: Story = {};

View file

@ -24,7 +24,20 @@ export default {
join(require.resolve('@skeletonlabs/skeleton'), '../**/*.{html,js,svelte,ts}') join(require.resolve('@skeletonlabs/skeleton'), '../**/*.{html,js,svelte,ts}')
], ],
theme: { theme: {
extend: {} extend: {},
fontFamily: {
mono: [
'ui-monospace',
'Spline Sans Mono',
'SFMono-Regular',
'Menlo',
'Monaco',
'Consolas',
'Liberation Mono',
'Courier New',
'monospace'
]
}
}, },
plugins: [require('@tailwindcss/typography'), forms, skeleton] plugins: [require('@tailwindcss/typography'), forms, skeleton]
}; };

View file

@ -0,0 +1,95 @@
/* Component test for SettingsKeys molecule.
* Copyright (C) 2024 André Jaenisch
* SPDX-FileCopyrightText: 2024 André Jaenisch
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/svelte';
import { init, locale, register } from 'svelte-i18n';
import SettingsKeys from '../../../src/lib/components/molecules/SettingsKeys.svelte';
import enMessages from '../../../src/lib/i18n/locales/en.json';
describe('SettingsKeys.svelte', () => {
beforeEach(() => {
register('en', () => import('../../../src/lib/i18n/locales/en.json'));
init({ fallbackLocale: 'en', initialLocale: 'en' });
locale.set('en');
});
it('should mount', () => {
// Arrange
// Nothing to prepare
// Act
const { container } = render(SettingsKeys);
// Assert
expect(container).toBeTruthy();
});
describe('SSH', () => {
it('should have a way to remove a SSH key', () => {
// Arrange
// Currently those keys are hardcoded
const numberOfKeys = 2 /* SSH */ + 1; /* GPG */
// Act
render(SettingsKeys);
// Assert
expect(
screen.getAllByRole('button', { name: enMessages.settings.ssh_gpg_keys.ssh.remove })
).toHaveLength(numberOfKeys);
});
it('should have a way to add a SSH key', () => {
// Arrange
// Nothing to prepare
// Act
render(SettingsKeys);
// Assert
expect(
screen.getByRole('button', { name: enMessages.settings.ssh_gpg_keys.ssh.add })
).toBeInTheDocument();
});
});
describe('GPG', () => {
it('should have a way to remove a GPG key', () => {
// Arrange
// Currently those keys are hardcoded
const numberOfKeys = 2 /* SSH */ + 1; /* GPG */
// Act
render(SettingsKeys);
// Assert
expect(
screen.getAllByRole('button', { name: enMessages.settings.ssh_gpg_keys.gpg.remove })
).toHaveLength(numberOfKeys);
});
it('should have a way to add a GPG key', () => {
// Arrange
// Nothing to prepare
// Act
render(SettingsKeys);
// Assert
expect(
screen.getByRole('button', { name: enMessages.settings.ssh_gpg_keys.gpg.add })
).toBeInTheDocument();
});
});
});