forked from btclock/webui
Improved input validation, added tests
This commit is contained in:
parent
cb9bfa4499
commit
e21b9895a7
4 changed files with 120 additions and 16 deletions
|
@ -42,9 +42,11 @@
|
||||||
"nostrRelay": "Nostr Relay",
|
"nostrRelay": "Nostr Relay",
|
||||||
"nostrZapNotify": "Nostr Zap Notifications",
|
"nostrZapNotify": "Nostr Zap Notifications",
|
||||||
"useNostr": "Use Nostr datasource",
|
"useNostr": "Use Nostr datasource",
|
||||||
"bitaxeHostname": "BitAxe hostname",
|
"bitaxeHostname": "BitAxe hostname or IP",
|
||||||
"bitaxeEnabled": "Enable BitAxe",
|
"bitaxeEnabled": "Enable BitAxe",
|
||||||
"nostrZapPubkey": "Nostr Zap pubkey"
|
"nostrZapPubkey": "Nostr Zap pubkey",
|
||||||
|
"invalidNostrPubkey": "Invalid Nostr pubkey, note that your pubkey does NOT start with npub.",
|
||||||
|
"convertingValidNpub": "Converting valid npub to pubkey"
|
||||||
},
|
},
|
||||||
"control": {
|
"control": {
|
||||||
"systemInfo": "System info",
|
"systemInfo": "System info",
|
||||||
|
|
|
@ -133,7 +133,7 @@
|
||||||
<div class="">
|
<div class="">
|
||||||
<Toast
|
<Toast
|
||||||
isOpen={toastIsOpen}
|
isOpen={toastIsOpen}
|
||||||
class="me-1 bg-{toastColor}"
|
class="me-1 bg-{toastColor} text-bg-{toastColor}"
|
||||||
autohide
|
autohide
|
||||||
on:close={() => (toastIsOpen = false)}
|
on:close={() => (toastIsOpen = false)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { isValidNostrRelay, getPubKey, isValidHexPubKey } from '$lib';
|
import { isValidNostrRelay, getPubKey, isValidHexPubKey, isValidNpub } from '$lib';
|
||||||
import { PUBLIC_BASE_URL } from '$lib/config';
|
import { PUBLIC_BASE_URL } from '$lib/config';
|
||||||
import { uiSettings } from '$lib/uiSettings';
|
import { uiSettings } from '$lib/uiSettings';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
@ -52,6 +52,10 @@
|
||||||
|
|
||||||
const onSave = async (e: Event) => {
|
const onSave = async (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
// const form = e.target as HTMLFormElement;
|
||||||
|
// const formData = new FormData(form);
|
||||||
|
|
||||||
let formSettings = $settings;
|
let formSettings = $settings;
|
||||||
|
|
||||||
delete formSettings['gitRev'];
|
delete formSettings['gitRev'];
|
||||||
|
@ -84,8 +88,49 @@
|
||||||
validNostrRelay = await isValidNostrRelay($settings.nostrRelay);
|
validNostrRelay = await isValidNostrRelay($settings.nostrRelay);
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkValidNostrPubkey = () => {
|
let validBitaxe = false;
|
||||||
$settings.nostrPubKey = getPubKey($settings.nostrPubKey);
|
const testBitaxe = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`http://${$settings.bitaxeHostname}/api/system/info`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
dispatch('showToast', {
|
||||||
|
color: 'danger',
|
||||||
|
text: `Failed to connect to BitAxe HTTP error! status: ${response.status}`
|
||||||
|
});
|
||||||
|
validBitaxe = false;
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const systemInfo = await response.json();
|
||||||
|
dispatch('showToast', {
|
||||||
|
color: 'success',
|
||||||
|
text: `Connected to BitAxe ${systemInfo.ASICModel} (Board version ${systemInfo.boardVersion}) running firmware ${systemInfo.version}.\r\nCurrent hashrate ${Math.round(systemInfo.hashRate)} GH/s`
|
||||||
|
});
|
||||||
|
validBitaxe = true;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof TypeError && error.message.includes('Failed to fetch')) {
|
||||||
|
dispatch('showToast', {
|
||||||
|
color: 'danger',
|
||||||
|
text: `Failed to connect to BitAxe, make sure you are connected to the same network.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.error('Failed to fetch Bitaxe system info:', error);
|
||||||
|
validBitaxe = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkValidNostrPubkey = (key) => {
|
||||||
|
if (isValidNpub($settings[key])) {
|
||||||
|
dispatch('showToast', {
|
||||||
|
color: 'info',
|
||||||
|
text: $_('section.settings.convertingValidNpub')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = getPubKey($settings[key]);
|
||||||
|
|
||||||
|
if (ret) $settings[key] = ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFlBrightnessChange = async () => {
|
const onFlBrightnessChange = async () => {
|
||||||
|
@ -160,6 +205,7 @@
|
||||||
id="timePerScreen"
|
id="timePerScreen"
|
||||||
min={1}
|
min={1}
|
||||||
step="1"
|
step="1"
|
||||||
|
required
|
||||||
bind:value={$settings.timePerScreen}
|
bind:value={$settings.timePerScreen}
|
||||||
/>
|
/>
|
||||||
<InputGroupText>{$_('time.minutes')}</InputGroupText>
|
<InputGroupText>{$_('time.minutes')}</InputGroupText>
|
||||||
|
@ -177,6 +223,7 @@
|
||||||
id="fullRefreshMin"
|
id="fullRefreshMin"
|
||||||
min={1}
|
min={1}
|
||||||
step="1"
|
step="1"
|
||||||
|
required
|
||||||
bind:value={$settings.fullRefreshMin}
|
bind:value={$settings.fullRefreshMin}
|
||||||
/>
|
/>
|
||||||
<InputGroupText>{$_('time.minutes')}</InputGroupText>
|
<InputGroupText>{$_('time.minutes')}</InputGroupText>
|
||||||
|
@ -212,6 +259,7 @@
|
||||||
step="1"
|
step="1"
|
||||||
name="tzOffset"
|
name="tzOffset"
|
||||||
id="tzOffset"
|
id="tzOffset"
|
||||||
|
required
|
||||||
bind:value={$settings.tzOffset}
|
bind:value={$settings.tzOffset}
|
||||||
/>
|
/>
|
||||||
<InputGroupText>{$_('time.minutes')}</InputGroupText>
|
<InputGroupText>{$_('time.minutes')}</InputGroupText>
|
||||||
|
@ -297,17 +345,23 @@
|
||||||
>{$_('section.settings.bitaxeHostname')}</Label
|
>{$_('section.settings.bitaxeHostname')}</Label
|
||||||
>
|
>
|
||||||
<Col md="6">
|
<Col md="6">
|
||||||
|
<InputGroup size={$uiSettings.inputSize}>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={$settings.bitaxeHostname}
|
bind:value={$settings.bitaxeHostname}
|
||||||
name="bitaxeHostname"
|
name="bitaxeHostname"
|
||||||
|
valid={validBitaxe}
|
||||||
id="bitaxeHostname"
|
id="bitaxeHostname"
|
||||||
bsSize={$uiSettings.inputSize}
|
required
|
||||||
></Input>
|
></Input>
|
||||||
|
<Button type="button" color="success" on:click={testBitaxe}
|
||||||
|
>{$_('test', { default: 'Test' })}</Button
|
||||||
|
>
|
||||||
|
</InputGroup>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{/if}
|
{/if}
|
||||||
{#if 'nostrZapNotify' in $settings}
|
{#if 'nostrZapNotify' in $settings && $settings['nostrZapNotify']}
|
||||||
<Row>
|
<Row>
|
||||||
<Label md={6} for="nostrZapPubkey" size={$uiSettings.inputSize}
|
<Label md={6} for="nostrZapPubkey" size={$uiSettings.inputSize}
|
||||||
>{$_('section.settings.nostrZapPubkey')}</Label
|
>{$_('section.settings.nostrZapPubkey')}</Label
|
||||||
|
@ -318,10 +372,15 @@
|
||||||
bind:value={$settings.nostrZapPubkey}
|
bind:value={$settings.nostrZapPubkey}
|
||||||
name="nostrZapPubkey"
|
name="nostrZapPubkey"
|
||||||
id="nostrZapPubkey"
|
id="nostrZapPubkey"
|
||||||
on:change={checkValidNostrPubkey}
|
on:change={() => checkValidNostrPubkey('nostrZapPubkey')}
|
||||||
invalid={!isValidHexPubKey($settings.nostrZapPubkey)}
|
invalid={!isValidHexPubKey($settings.nostrZapPubkey)}
|
||||||
bsSize={$uiSettings.inputSize}
|
bsSize={$uiSettings.inputSize}
|
||||||
|
required
|
||||||
|
minlength="64"
|
||||||
></Input>
|
></Input>
|
||||||
|
{#if !isValidHexPubKey($settings.nostrZapPubkey)}
|
||||||
|
<FormText>{$_('section.settings.invalidNostrPubkey')}</FormText>
|
||||||
|
{/if}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -336,10 +395,13 @@
|
||||||
bind:value={$settings.nostrPubKey}
|
bind:value={$settings.nostrPubKey}
|
||||||
name="nostrPubKey"
|
name="nostrPubKey"
|
||||||
id="nostrPubKey"
|
id="nostrPubKey"
|
||||||
on:change={checkValidNostrPubkey}
|
on:change={() => checkValidNostrPubkey('nostrPubKey')}
|
||||||
invalid={!isValidHexPubKey($settings.nostrPubKey)}
|
invalid={!isValidHexPubKey($settings.nostrPubKey)}
|
||||||
bsSize={$uiSettings.inputSize}
|
bsSize={$uiSettings.inputSize}
|
||||||
></Input>
|
></Input>
|
||||||
|
{#if !isValidHexPubKey($settings.nostrPubKey)}
|
||||||
|
<FormText>{$_('section.settings.invalidNostrPubkey')}</FormText>
|
||||||
|
{/if}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -357,6 +419,7 @@
|
||||||
id="nostrRelay"
|
id="nostrRelay"
|
||||||
valid={validNostrRelay}
|
valid={validNostrRelay}
|
||||||
bsSize={$uiSettings.inputSize}
|
bsSize={$uiSettings.inputSize}
|
||||||
|
required
|
||||||
></Input>
|
></Input>
|
||||||
<Button type="button" color="success" on:click={testNostrRelay}
|
<Button type="button" color="success" on:click={testNostrRelay}
|
||||||
>{$_('test', { default: 'Test' })}</Button
|
>{$_('test', { default: 'Test' })}</Button
|
||||||
|
@ -378,6 +441,7 @@
|
||||||
id="mempoolInstance"
|
id="mempoolInstance"
|
||||||
disabled={$settings.ownDataSource}
|
disabled={$settings.ownDataSource}
|
||||||
bsSize="sm"
|
bsSize="sm"
|
||||||
|
required
|
||||||
></Input>
|
></Input>
|
||||||
<InputGroupText>
|
<InputGroupText>
|
||||||
<Input
|
<Input
|
||||||
|
@ -404,6 +468,8 @@
|
||||||
name="hostnamePrefix"
|
name="hostnamePrefix"
|
||||||
id="hostnamePrefix"
|
id="hostnamePrefix"
|
||||||
bsSize={$uiSettings.inputSize}
|
bsSize={$uiSettings.inputSize}
|
||||||
|
required
|
||||||
|
minlength="1"
|
||||||
></Input>
|
></Input>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -439,6 +505,7 @@
|
||||||
min={1}
|
min={1}
|
||||||
step="1"
|
step="1"
|
||||||
bind:value={$settings.wpTimeout}
|
bind:value={$settings.wpTimeout}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<InputGroupText>{$_('time.seconds')}</InputGroupText>
|
<InputGroupText>{$_('time.seconds')}</InputGroupText>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
|
@ -44,6 +44,13 @@ const settingsJson = {
|
||||||
ip: '192.168.20.231',
|
ip: '192.168.20.231',
|
||||||
txPower: 78,
|
txPower: 78,
|
||||||
gitRev: '25d8b92bcbc8938417c140355ea3ba99ff9eb4b7',
|
gitRev: '25d8b92bcbc8938417c140355ea3ba99ff9eb4b7',
|
||||||
|
gitTag: '3.1.9',
|
||||||
|
bitaxeEnabled: false,
|
||||||
|
bitaxeHostname: 'bitaxe1',
|
||||||
|
nostrZapNotify: true,
|
||||||
|
hwRev: 'REV_A_EPD_2_13',
|
||||||
|
fsRev: '4c5d9616212b27e3f05c35370f0befcf2c5a04b2',
|
||||||
|
nostrZapPubkey: 'b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422',
|
||||||
lastBuildTime: '1700666677',
|
lastBuildTime: '1700666677',
|
||||||
screens: [
|
screens: [
|
||||||
{ id: 0, name: 'Block Height', enabled: true },
|
{ id: 0, name: 'Block Height', enabled: true },
|
||||||
|
@ -187,6 +194,34 @@ test('info message when fetch eur price is enabled', async ({ page }) => {
|
||||||
await expect(page.getByText('the WS Price connection will show')).toBeVisible();
|
await expect(page.getByText('the WS Price connection will show')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('npub values will be converted to hex pubkeys', async ({ page }) => {
|
||||||
|
await page.goto('/');
|
||||||
|
|
||||||
|
for (const field of ['#nostrZapPubkey']) {
|
||||||
|
for (const val of ['npub1k5f85zx0xdskyayqpfpc0zq6n7vwqjuuxugkayk72fgynp34cs3qfcvqg2']) {
|
||||||
|
await page.fill(field, val);
|
||||||
|
|
||||||
|
await page.getByLabel('Nostr Relay').click();
|
||||||
|
const resultValue = await page.$eval(field, (input: HTMLInputElement) => input.value);
|
||||||
|
|
||||||
|
expect(resultValue).toBe('b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('empty nostr relay field is not accepted', async ({ page }) => {
|
||||||
|
await page.goto('/');
|
||||||
|
|
||||||
|
const nostrRelayField = page.getByLabel('Nostr Relay');
|
||||||
|
|
||||||
|
nostrRelayField.fill('');
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
const validationMessage = await nostrRelayField.evaluate((el) => el.validationMessage);
|
||||||
|
|
||||||
|
expect(validationMessage).toContain('Please fill out this field');
|
||||||
|
});
|
||||||
|
|
||||||
test('screens should be able to change', async ({ page }) => {
|
test('screens should be able to change', async ({ page }) => {
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
await expect(page.getByRole('button', { name: 'Sats per Dollar' })).toBeVisible();
|
await expect(page.getByRole('button', { name: 'Sats per Dollar' })).toBeVisible();
|
||||||
|
|
Loading…
Reference in a new issue