mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-08-04 19:25:13 -04:00
add new device form
This commit is contained in:
@@ -31,7 +31,7 @@ Open up [http://localhost:5173/](http://localhost:5173/)
|
||||
- frontend
|
||||
|
||||
- [x] add rest of settings page
|
||||
- [ ] form for adding new devices
|
||||
- [x] form for adding new devices
|
||||
- [ ] add per device settings
|
||||
- [x] ~~theme toggle button~~
|
||||
- [x] ~~add device ports to cards~~
|
||||
|
||||
@@ -13,8 +13,8 @@ func init() {
|
||||
m.Register(func(db dbx.Builder) error {
|
||||
jsonData := `{
|
||||
"id": "z5lghx2r3tm45n1",
|
||||
"created": "2023-01-23 19:32:19.965Z",
|
||||
"updated": "2023-01-23 19:32:19.965Z",
|
||||
"created": "2023-01-26 14:28:42.378Z",
|
||||
"updated": "2023-01-26 14:28:42.378Z",
|
||||
"name": "devices",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
@@ -111,8 +111,8 @@ func init() {
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "1a7yrwo9",
|
||||
"name": "shutdown",
|
||||
"id": "kzvdbbws",
|
||||
"name": "wake_cron",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"unique": false,
|
||||
@@ -124,8 +124,34 @@ func init() {
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "kzvdbbws",
|
||||
"name": "wake",
|
||||
"id": "91bs6clw",
|
||||
"name": "shutdown_cron",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "1a7yrwo9",
|
||||
"name": "shutdown_cmd",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "iarksm3l",
|
||||
"name": "password",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"unique": false,
|
||||
@@ -13,8 +13,8 @@ func init() {
|
||||
m.Register(func(db dbx.Builder) error {
|
||||
jsonData := `{
|
||||
"id": "cti4l8f4mz8df3r",
|
||||
"created": "2023-01-23 19:32:19.965Z",
|
||||
"updated": "2023-01-23 19:32:19.965Z",
|
||||
"created": "2023-01-26 14:28:42.378Z",
|
||||
"updated": "2023-01-26 14:28:42.378Z",
|
||||
"name": "ports",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
@@ -13,8 +13,8 @@ func init() {
|
||||
m.Register(func(db dbx.Builder) error {
|
||||
jsonData := `{
|
||||
"id": "nmj3ko20gzkg8n3",
|
||||
"created": "2023-01-23 19:32:19.965Z",
|
||||
"updated": "2023-01-23 19:32:19.965Z",
|
||||
"created": "2023-01-26 14:28:42.378Z",
|
||||
"updated": "2023-01-26 14:28:42.378Z",
|
||||
"name": "settings",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
@@ -22,8 +22,8 @@ func init() {
|
||||
}, func(db dbx.Builder) error {
|
||||
jsonData := `{
|
||||
"id": "_pb_users_auth_",
|
||||
"created": "2023-01-23 19:31:58.918Z",
|
||||
"updated": "2023-01-23 19:31:58.919Z",
|
||||
"created": "2023-01-26 14:28:20.206Z",
|
||||
"updated": "2023-01-26 14:28:20.206Z",
|
||||
"name": "users",
|
||||
"type": "auth",
|
||||
"system": false,
|
||||
@@ -1,14 +1,40 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { pocketbase } from '@stores/pocketbase';
|
||||
import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
|
||||
import Fa from 'svelte-fa';
|
||||
|
||||
let pb;
|
||||
let files;
|
||||
let settings = {};
|
||||
let buttons = {
|
||||
saved: 'none',
|
||||
restore: 'none'
|
||||
}
|
||||
add_device: {
|
||||
state: 'none',
|
||||
error: ''
|
||||
},
|
||||
saved: {
|
||||
state: 'none',
|
||||
error: ''
|
||||
},
|
||||
restore: {
|
||||
state: 'none',
|
||||
error: ''
|
||||
}
|
||||
};
|
||||
let newDevice = {
|
||||
name: '',
|
||||
ip: '',
|
||||
mac: '',
|
||||
netmask: '255.255.255.0',
|
||||
link: '',
|
||||
wake_cron: '',
|
||||
shutdown_cron: '',
|
||||
shutdown_cmd: '',
|
||||
password: ''
|
||||
};
|
||||
|
||||
let passwordShow = false;
|
||||
$: passwordType = passwordShow ? 'text' : 'password';
|
||||
|
||||
onMount(async () => {
|
||||
pocketbase.subscribe((conn) => {
|
||||
@@ -19,10 +45,28 @@
|
||||
settings = result.items[0];
|
||||
});
|
||||
|
||||
async function saveSettings() {
|
||||
buttons.saved = 'waiting';
|
||||
async function addDevice() {
|
||||
buttons.add_device.state = 'waiting';
|
||||
try {
|
||||
const result = await pb.collection('settings').update(
|
||||
await pb.collection('devices').create(newDevice, { $autoCancel: false });
|
||||
setTimeout(() => {
|
||||
buttons.add_device.state = 'none';
|
||||
}, 3000);
|
||||
buttons.add_device.state = 'success';
|
||||
} catch (error) {
|
||||
setTimeout(() => {
|
||||
buttons.add_device.error = '';
|
||||
buttons.add_device.state = 'none';
|
||||
}, 3000);
|
||||
buttons.add_device.error = error;
|
||||
buttons.add_device.state = 'failed';
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSettings() {
|
||||
buttons.saved.state = 'waiting';
|
||||
try {
|
||||
await pb.collection('settings').update(
|
||||
settings.id,
|
||||
{
|
||||
interval: settings.interval,
|
||||
@@ -31,19 +75,21 @@
|
||||
{ $autoCancel: false }
|
||||
);
|
||||
setTimeout(() => {
|
||||
buttons.saved = 'none';
|
||||
}, 2000);
|
||||
buttons.saved = 'success';
|
||||
buttons.saved.state = 'none';
|
||||
}, 3000);
|
||||
buttons.saved.state = 'success';
|
||||
} catch (error) {
|
||||
setTimeout(() => {
|
||||
buttons.saved = 'none';
|
||||
}, 2000);
|
||||
buttons.saved = 'failed';
|
||||
buttons.saved.error = '';
|
||||
buttons.saved.state = 'none';
|
||||
}, 3000);
|
||||
buttons.saved.error = error;
|
||||
buttons.saved.state = 'failed';
|
||||
}
|
||||
}
|
||||
|
||||
async function restoreV2Backup() {
|
||||
buttons.restore = 'waiting'
|
||||
buttons.restore.state = 'waiting';
|
||||
|
||||
try {
|
||||
// delete all devices in pocketbase
|
||||
@@ -101,19 +147,181 @@
|
||||
}
|
||||
};
|
||||
setTimeout(() => {
|
||||
buttons.restore = 'none';
|
||||
}, 2000);
|
||||
buttons.restore = 'success';
|
||||
buttons.restore.state = 'none';
|
||||
}, 3000);
|
||||
buttons.restore.state = 'success';
|
||||
} catch (error) {
|
||||
setTimeout(() => {
|
||||
buttons.restore = 'none';
|
||||
}, 2000);
|
||||
buttons.restore = 'failed';
|
||||
buttons.restore.error = '';
|
||||
buttons.restore.state = 'none';
|
||||
}, 3000);
|
||||
buttons.restore.error = error;
|
||||
buttons.restore.state = 'failed';
|
||||
}
|
||||
}
|
||||
|
||||
function onPasswordInput(event) {
|
||||
newDevice.password = event.target.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<section class="m-0 mt-4 p-4 shadow-sm">
|
||||
<h3 class="mb-3">Add new device</h3>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form on:submit|preventDefault={addDevice}>
|
||||
<h5>Required:</h5>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">Name</span>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="Office Pc"
|
||||
aria-label="Name"
|
||||
aria-describedby="addon-wrapping"
|
||||
type="text"
|
||||
required
|
||||
bind:value={newDevice.name}
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">IP</span>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="192.168.1.34"
|
||||
aria-label="IP"
|
||||
aria-describedby="addon-wrapping"
|
||||
type="text"
|
||||
required
|
||||
bind:value={newDevice.ip}
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">MAC</span>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="aa:bb:cc:dd:ee:ff"
|
||||
aria-label="MAC"
|
||||
aria-describedby="addon-wrapping"
|
||||
type="text"
|
||||
required
|
||||
bind:value={newDevice.mac}
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">Netmask</span>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="Most likely 255.255.255.0 or 255.255.0.0"
|
||||
aria-label="Netmask"
|
||||
aria-describedby="addon-wrapping"
|
||||
type="text"
|
||||
required
|
||||
bind:value={newDevice.netmask}
|
||||
/>
|
||||
</div>
|
||||
<h5 class="mt-4">Optional:</h5>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">Link</span>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="Clickable link on device card"
|
||||
aria-label="Link"
|
||||
aria-describedby="addon-wrapping"
|
||||
type="url"
|
||||
bind:value={newDevice.link}
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">Wake Cron<sup>(1)</sup></span>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="Automatically wake device with cron"
|
||||
aria-label="Wake Cron"
|
||||
aria-describedby="addon-wrapping"
|
||||
type="text"
|
||||
bind:value={newDevice.wake_cron}
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">Shutdown Cron<sup>(1)</sup></span>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="Automatically shutdown device with cron"
|
||||
aria-label="Shutdown Cron"
|
||||
aria-describedby="addon-wrapping"
|
||||
type="text"
|
||||
bind:value={newDevice.shutdown_cron}
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">Shutdown Cmd<sup>(2)</sup></span>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="Command to shutdown device"
|
||||
aria-label="Shutdown Cmd"
|
||||
aria-describedby="addon-wrapping"
|
||||
type="text"
|
||||
bind:value={newDevice.shutdown_cmd}
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">Password<sup>(3)</sup></span>
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="BIOS password for wol"
|
||||
aria-label="IP"
|
||||
aria-describedby="addon-wrapping"
|
||||
type={passwordType}
|
||||
value={newDevice.password}
|
||||
on:input={onPasswordInput}
|
||||
/>
|
||||
<button
|
||||
class="btn btn-outline-secondary"
|
||||
type="button"
|
||||
on:click={() => (passwordShow = !passwordShow)}
|
||||
>
|
||||
<Fa icon={passwordShow ? faEyeSlash : faEye} />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-secondary"
|
||||
class:btn-success={buttons.add_device.state === 'success' ? true : false}
|
||||
class:btn-warning={buttons.add_device.state === 'waiting' ? true : false}
|
||||
class:btn-danger={buttons.add_device.state === 'failed' ? true : false}
|
||||
>
|
||||
{#if buttons.add_device.state === 'none'}
|
||||
Add device
|
||||
{:else if buttons.add_device.state === 'success'}
|
||||
Added successfully
|
||||
{:else if buttons.add_device.state === 'waiting'}
|
||||
Waiting
|
||||
{:else if buttons.add_device.state === 'failed'}
|
||||
Failed: {buttons.add_device.error}
|
||||
{/if}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="callout callout-info mb-0">
|
||||
<h5>Optional:</h5>
|
||||
<p class="m-0">(1) Same cron syntax as for ping interval.</p>
|
||||
<p class="m-0">(2) Shell command to be executed. e.g.:</p>
|
||||
<ul>
|
||||
<li>Windows: "net rpc shutdown -I 192.168.1.13 -U test%test"</li>
|
||||
<li>
|
||||
Linux: "sshpass -p your_password ssh -o 'StrictHostKeyChecking=no' user@hostname 'sudo
|
||||
shutdown'"
|
||||
</li>
|
||||
</ul>
|
||||
<p class="m-0">
|
||||
(3) Some network cards have the option to set a password for magic packets.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="m-0 mt-4 p-4 shadow-sm">
|
||||
<h3 class="mb-3">Ping interval</h3>
|
||||
<div class="row">
|
||||
@@ -122,7 +330,6 @@
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">Cron</span>
|
||||
<input
|
||||
id="settings-interval"
|
||||
class="form-control"
|
||||
placeholder="e.g. '@every 5s' or '@every 1m'"
|
||||
aria-label="Interval"
|
||||
@@ -147,9 +354,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="m-0 mt-4 p-4 shadow-sm">
|
||||
<h3 class="mb-3">Notifications</h3>
|
||||
<h3 class="my-3">Notifications</h3>
|
||||
<p>Show toast notifications in the bottom right corner.</p>
|
||||
<div class="form-check form-switch">
|
||||
<label class="form-check-label" for="settings-notifications">Enable notifications</label>
|
||||
@@ -161,33 +366,31 @@
|
||||
bind:checked={settings.notifications}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<div class="m-0 mt-4 shadow-sm">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary w-100"
|
||||
class:btn-success={buttons.saved === 'success' ? true : false}
|
||||
class:btn-warning={buttons.saved === 'waiting' ? true : false}
|
||||
class:btn-danger={buttons.saved === 'failed' ? true : false}
|
||||
class="btn btn-secondary mt-3"
|
||||
class:btn-success={buttons.saved.state === 'success' ? true : false}
|
||||
class:btn-warning={buttons.saved.state === 'waiting' ? true : false}
|
||||
class:btn-danger={buttons.saved.state === 'failed' ? true : false}
|
||||
on:click={() => saveSettings()}
|
||||
>
|
||||
{#if buttons.saved === 'none'}
|
||||
{#if buttons.saved.state === 'none'}
|
||||
Save
|
||||
{:else if buttons.saved === 'success'}
|
||||
{:else if buttons.saved.state === 'success'}
|
||||
Saved
|
||||
{:else if buttons.saved === 'waiting'}
|
||||
{:else if buttons.saved.state === 'waiting'}
|
||||
Waiting
|
||||
{:else if buttons.saved === 'failed'}
|
||||
Failed
|
||||
{:else if buttons.saved.state === 'failed'}
|
||||
Failed: {buttons.add_device.error}
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
<section class="m-0 mt-5 p-4 shadow-sm">
|
||||
</section>
|
||||
<section class="m-0 my-4 p-4 shadow-sm">
|
||||
<h3 class="mb-3">Restore</h3>
|
||||
<p>If you had UpSnap v2.3.2 (or higher) running before, you can restore your devices here.</p>
|
||||
<div class="callout callout-danger fw-bold">This will wipe the existing database!</div>
|
||||
<input
|
||||
class="form-control mb-3"
|
||||
class="form-control"
|
||||
placeholder="Username"
|
||||
aria-label="Username"
|
||||
aria-describedby="addon-wrapping"
|
||||
@@ -197,21 +400,21 @@
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
class:btn-success={buttons.restore === 'success' ? true : false}
|
||||
class:btn-warning={buttons.restore === 'waiting' ? true : false}
|
||||
class:btn-danger={buttons.restore === 'failed' ? true : false}
|
||||
class="btn btn-secondary mt-3"
|
||||
class:btn-success={buttons.restore.state === 'success' ? true : false}
|
||||
class:btn-warning={buttons.restore.state === 'waiting' ? true : false}
|
||||
class:btn-danger={buttons.restore.state === 'failed' ? true : false}
|
||||
disabled={files ? false : true}
|
||||
on:click={() => restoreV2Backup()}
|
||||
>
|
||||
{#if buttons.restore === 'none'}
|
||||
{#if buttons.restore.state === 'none'}
|
||||
Restore
|
||||
{:else if buttons.restore === 'success'}
|
||||
{:else if buttons.restore.state === 'success'}
|
||||
Restored
|
||||
{:else if buttons.restore === 'waiting'}
|
||||
{:else if buttons.restore.state === 'waiting'}
|
||||
Waiting
|
||||
{:else if buttons.restore === 'failed'}
|
||||
Failed
|
||||
{:else if buttons.restore.state === 'failed'}
|
||||
Failed: {buttons.restore.error}
|
||||
{/if}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user