mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-07-22 21:43:42 -04:00
more changes to device settings
This commit is contained in:
@@ -54,7 +54,7 @@
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "eslint --fix --cache --ignore-path ../.gitignore .",
|
||||
"format": "prettier --write --cache --cache-strategy content --ignore-path ../.gitignore --plugin-search-dir=. .",
|
||||
"prepare": "husky install"
|
||||
"prepare": "cd .. && husky install frontend/.husky"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-static": "^2.0.0",
|
||||
|
||||
1875
frontend/pnpm-lock.yaml
generated
1875
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
30
frontend/src/lib/components/Alert.svelte
Normal file
30
frontend/src/lib/components/Alert.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { scale } from 'svelte/transition';
|
||||
import { tweened } from 'svelte/motion';
|
||||
|
||||
import Fa from 'svelte-fa';
|
||||
import type { IconDefinition } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export let color: string;
|
||||
export let message: string;
|
||||
export let icon: IconDefinition;
|
||||
export let customClasses = '';
|
||||
export let duration = 0;
|
||||
|
||||
const value = tweened(0, {
|
||||
duration: duration
|
||||
});
|
||||
value.set(duration);
|
||||
</script>
|
||||
|
||||
<div class="alert alert-{color} {customClasses}" transition:scale={{ delay: 0, duration: 200 }}>
|
||||
<div class="flex flex-col text-left">
|
||||
{#if icon}
|
||||
<Fa {icon} />
|
||||
{/if}
|
||||
<span>{message}</span>
|
||||
{#if duration !== 0}
|
||||
<progress class="progress" value={duration - $value} max={duration} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -10,7 +10,6 @@
|
||||
<div class="card-body p-6">
|
||||
<h1 class="card-title">{device.name}</h1>
|
||||
<ul class="menu bg-base-200 rounded-box">
|
||||
<li class="menu-title">Interfaces</li>
|
||||
<!-- TODO: change to nic array once backend supports it -->
|
||||
<DeviceCardNic {device} />
|
||||
</ul>
|
||||
@@ -21,6 +20,7 @@
|
||||
addSuffix: true
|
||||
})}
|
||||
</span>
|
||||
<a class="btn btn-sm btn-neutral ms-auto" href="/device/{device.id}">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,17 +25,15 @@
|
||||
</script>
|
||||
|
||||
<li>
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="flex items-start p-2 gap-4">
|
||||
<div>
|
||||
{#if device.status === 'offline'}
|
||||
<button
|
||||
class="btn btn-success flex-shrink"
|
||||
on:click={() => wake()}
|
||||
on:keydown={() => wake()}><Fa icon={faPowerOff} /></button
|
||||
<button class="btn btn-error flex-shrink" on:click={() => wake()} on:keydown={() => wake()}
|
||||
><Fa icon={faPowerOff} /></button
|
||||
>
|
||||
{:else if device.status === 'online'}
|
||||
<button
|
||||
class="btn btn-error flex-shrink"
|
||||
class="btn btn-success flex-shrink"
|
||||
on:click={() => shutdown()}
|
||||
on:keydown={() => shutdown()}><Fa icon={faPowerOff} /></button
|
||||
>
|
||||
@@ -49,17 +47,19 @@
|
||||
<div class="text-lg font-bold leading-4">{device.ip}</div>
|
||||
<div class="">{device.mac}</div>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
{#each device.expand.ports as port}
|
||||
<span class="flex items-center gap-1">
|
||||
{#if port.status}
|
||||
<Fa icon={faCircle} class="bg-error" />
|
||||
{port.name} ({port.number})
|
||||
{:else}
|
||||
<Fa icon={faCircle} class="text-error" />
|
||||
{port.name} ({port.number})
|
||||
{/if}
|
||||
</span>
|
||||
{/each}
|
||||
{#if device?.expand?.ports}
|
||||
{#each device?.expand?.ports as port}
|
||||
<span class="flex items-center gap-1 break-all">
|
||||
{#if port.status}
|
||||
<Fa icon={faCircle} class="bg-error" />
|
||||
{port.name} ({port.number})
|
||||
{:else}
|
||||
<Fa icon={faCircle} class="text-error" />
|
||||
{port.name} ({port.number})
|
||||
{/if}
|
||||
</span>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
76
frontend/src/lib/components/DeviceFormPort.svelte
Normal file
76
frontend/src/lib/components/DeviceFormPort.svelte
Normal file
@@ -0,0 +1,76 @@
|
||||
<script lang="ts">
|
||||
import { pocketbase } from '$lib/stores/pocketbase';
|
||||
import { scale } from 'svelte/transition';
|
||||
import Fa from 'svelte-fa';
|
||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||
import type { Device, Port } from '$lib/types/device';
|
||||
import type { Record } from 'pocketbase';
|
||||
|
||||
export let changes: Device;
|
||||
export let index: number;
|
||||
export let portErrMsg: string;
|
||||
export let portErrTimeout: number;
|
||||
|
||||
function deletePort(port: Port | Record) {
|
||||
if (port.id !== undefined) {
|
||||
$pocketbase
|
||||
.collection('ports')
|
||||
.delete(port.id)
|
||||
.then(() => {
|
||||
changes.ports = changes.ports.filter((id: number) => id !== changes.ports[index]);
|
||||
})
|
||||
.catch((err) => {
|
||||
clearTimeout(portErrTimeout);
|
||||
portErrTimeout = setTimeout(() => {
|
||||
portErrMsg = '';
|
||||
}, 10000);
|
||||
portErrMsg = err;
|
||||
});
|
||||
}
|
||||
changes.expand.ports = changes.expand.ports.filter(
|
||||
(p) => p.id !== changes.expand.ports[index].id
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if changes.expand.ports[index]}
|
||||
<div class="card bg-base-100 shadow-xl" transition:scale={{ delay: 0, duration: 200 }}>
|
||||
<div class="card-body p-3">
|
||||
<div class="flex flex-row gap-2">
|
||||
<div>
|
||||
<label class="label p-0" for="port-name-{index}">
|
||||
<span class="label-text">Name</span>
|
||||
</label>
|
||||
<input
|
||||
id="port-name-{index}"
|
||||
type="text"
|
||||
placeholder="ssh"
|
||||
class="input input-sm input-bordered w-full max-w-xs"
|
||||
bind:value={changes.expand.ports[index].name}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="label p-0" for="port-number-{index}">
|
||||
<span class="label-text">Number</span>
|
||||
</label>
|
||||
<input
|
||||
id="port-number-{index}"
|
||||
type="number"
|
||||
placeholder="22"
|
||||
min="1"
|
||||
max="65535"
|
||||
class="input input-sm input-bordered w-24"
|
||||
bind:value={changes.expand.ports[index].number}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions justify-end">
|
||||
<button
|
||||
class="btn btn-xs btn-error"
|
||||
on:click={() => deletePort(changes.expand.ports[index])}
|
||||
type="button"><Fa icon={faTrash} />Delete</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { pocketbase } from '$lib/stores/pocketbase';
|
||||
import { pocketbase, backendUrl } from '$lib/stores/pocketbase';
|
||||
import { settingsPub } from '$lib/stores/settings';
|
||||
import Fa from 'svelte-fa';
|
||||
import { faCog, faHome, faCircleQuestion, faKey } from '@fortawesome/free-solid-svg-icons';
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<div class="navbar bg-base-100">
|
||||
<div class="navbar-start">
|
||||
<div class="dropdown">
|
||||
<a class="btn btn-ghost normal-case text-xl" href="/">
|
||||
<img
|
||||
src={$settingsPub?.collectionId && $settingsPub?.favicon
|
||||
? `/api/files/settings_public/${$settingsPub?.collectionId}/${$settingsPub?.favicon}`
|
||||
@@ -26,7 +26,7 @@
|
||||
width="45"
|
||||
height="45"
|
||||
/>{$settingsPub?.website_title ? $settingsPub?.website_title : ''}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-center hidden md:flex">
|
||||
<ul class="menu menu-horizontal px-1">
|
||||
@@ -51,10 +51,7 @@
|
||||
<div class="dropdown dropdown-end">
|
||||
<label tabindex="-1" class="btn btn-ghost btn-circle avatar" for="avatar">
|
||||
<div class="w-10 rounded-full" id="avatar">
|
||||
<img
|
||||
src="{$pocketbase.baseUrl}/_/images/avatars/avatar{avatar}.svg"
|
||||
alt="Profile pic"
|
||||
/>
|
||||
<img src="{backendUrl}_/images/avatars/avatar{avatar}.svg" alt="Profile pic" />
|
||||
</div>
|
||||
</label>
|
||||
<ul
|
||||
|
||||
@@ -3,7 +3,7 @@ import PocketBase from 'pocketbase';
|
||||
import type { Device } from '$lib/types/device';
|
||||
|
||||
// set backend url based on environment
|
||||
export const backendUrl = import.meta.env.DEV ? 'http://127.0.0.1:8090' : '';
|
||||
export const backendUrl = import.meta.env.DEV ? 'http://127.0.0.1:8090/' : '/';
|
||||
|
||||
// connect to backend
|
||||
const pb = new PocketBase(backendUrl);
|
||||
|
||||
@@ -5,3 +5,8 @@ export type Device = Record & {
|
||||
ports: Record[];
|
||||
};
|
||||
};
|
||||
|
||||
export type Port = Record & {
|
||||
name: string;
|
||||
number: number;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { pocketbase, backendUrl, devices } from '$lib/stores/pocketbase';
|
||||
import { settingsPub } from '$lib/stores/settings';
|
||||
import Navbar from '$lib/components/Navbar.svelte';
|
||||
import type { Device } from '$lib/types/device';
|
||||
|
||||
onMount(async () => {
|
||||
// set settingsPub store on load
|
||||
@@ -47,7 +48,7 @@
|
||||
.collection('devices')
|
||||
.getFullList(1000, { sort: 'name', expand: 'ports,groups' })
|
||||
.then((data) => {
|
||||
devices.set(data);
|
||||
devices.set(data as Device[]);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -65,6 +66,6 @@
|
||||
<Navbar />
|
||||
{/if}
|
||||
|
||||
<div class="container mx-auto">
|
||||
<div class="container mx-auto p-2">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,46 @@
|
||||
<script lang="ts">
|
||||
import DeviceCard from '$lib/components/DeviceCard.svelte';
|
||||
import { devices } from '$lib/stores/pocketbase';
|
||||
import { pocketbase, devices } from '$lib/stores/pocketbase';
|
||||
import type { Device } from '$lib/types/device';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
$pocketbase.collection('devices').subscribe('*', (e) => {
|
||||
const index = $devices.findIndex((device) => device.id === e.record.id);
|
||||
if (e.action === 'create') {
|
||||
$devices = [...$devices, e.record as Device];
|
||||
} else if (e.action === 'update') {
|
||||
// get expanded values before updating the store
|
||||
$pocketbase
|
||||
.collection('devices')
|
||||
.getOne(e.record.id, {
|
||||
expand: 'ports,groups'
|
||||
})
|
||||
.then((data) => {
|
||||
$devices[index] = data as Device;
|
||||
});
|
||||
} else if (e.action === 'delete') {
|
||||
delete $devices[index];
|
||||
}
|
||||
});
|
||||
|
||||
$pocketbase.collection('ports').subscribe('*', (e) => {
|
||||
if (e.action === 'update') {
|
||||
// replace port in device store by finding both indexes
|
||||
let portIndex = -1;
|
||||
const deviceIndex = $devices.findIndex((device) =>
|
||||
device.expand.ports.map((port) => {
|
||||
if (port.id === e.record.id) {
|
||||
portIndex = device.expand.ports.indexOf(port);
|
||||
}
|
||||
})
|
||||
);
|
||||
if (deviceIndex !== -1 && portIndex !== -1) {
|
||||
$devices[deviceIndex].expand.ports[portIndex] = e.record;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{#each $devices as device}
|
||||
|
||||
254
frontend/src/routes/device/[id]/+page.svelte
Normal file
254
frontend/src/routes/device/[id]/+page.svelte
Normal file
@@ -0,0 +1,254 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { pocketbase, devices } from '$lib/stores/pocketbase';
|
||||
import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
|
||||
import Alert from '$lib/components/Alert.svelte';
|
||||
import DeviceFormPort from '$lib/components/DeviceFormPort.svelte';
|
||||
import type { Device, Port } from '$lib/types/device';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let waitingForDevices = true;
|
||||
let device: Device | undefined;
|
||||
let changes: Device | undefined;
|
||||
|
||||
let portErrMsg = '';
|
||||
let portErrTimeout: number;
|
||||
|
||||
let saveErrMsg = '';
|
||||
let saveErrTimeout: number;
|
||||
|
||||
devices.subscribe((data) => {
|
||||
if (waitingForDevices === false) return;
|
||||
if (data.length === 0) return;
|
||||
|
||||
device = data.find((dev) => dev.id === $page.params.id);
|
||||
if (device !== undefined) {
|
||||
changes = device;
|
||||
if (changes.expand.ports === undefined) {
|
||||
changes.expand.ports = [];
|
||||
}
|
||||
}
|
||||
waitingForDevices = false;
|
||||
});
|
||||
|
||||
async function saveDevice() {
|
||||
if (device === undefined || changes === undefined) return;
|
||||
|
||||
// create/update all ports
|
||||
let portIds: string[] = [];
|
||||
await Promise.all(
|
||||
changes.expand.ports.map(async (port) => {
|
||||
if (port.id === undefined) {
|
||||
const data = await createPort(port as Port);
|
||||
if (data === undefined) return;
|
||||
portIds = [...portIds, data.id];
|
||||
} else {
|
||||
const data = await updatePort(port as Port);
|
||||
if (data === undefined) return;
|
||||
portIds = [...portIds, data.id];
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
changes.ports = portIds;
|
||||
$pocketbase
|
||||
.collection('devices')
|
||||
.update(device.id, changes)
|
||||
.then(() => {
|
||||
goto('/');
|
||||
})
|
||||
.catch((err) => {
|
||||
clearTimeout(saveErrTimeout);
|
||||
saveErrTimeout = setTimeout(() => {
|
||||
saveErrMsg = '';
|
||||
}, 10000);
|
||||
saveErrMsg = err;
|
||||
});
|
||||
}
|
||||
|
||||
async function createPort(port: Port): Promise<Port | undefined> {
|
||||
let newport: Port | undefined;
|
||||
await $pocketbase
|
||||
.collection('ports')
|
||||
.create(port)
|
||||
.then((data) => {
|
||||
newport = data as Port;
|
||||
})
|
||||
.catch((err) => {
|
||||
clearTimeout(saveErrTimeout);
|
||||
saveErrTimeout = setTimeout(() => {
|
||||
saveErrMsg = '';
|
||||
}, 10000);
|
||||
saveErrMsg = err;
|
||||
newport = undefined;
|
||||
});
|
||||
return newport;
|
||||
}
|
||||
|
||||
async function updatePort(port: Port): Promise<Port | undefined> {
|
||||
let newport: Port | undefined;
|
||||
await $pocketbase
|
||||
.collection('ports')
|
||||
.update(port.id, port)
|
||||
.then((data) => {
|
||||
newport = data as Port;
|
||||
})
|
||||
.catch((err) => {
|
||||
clearTimeout(saveErrTimeout);
|
||||
saveErrTimeout = setTimeout(() => {
|
||||
saveErrMsg = '';
|
||||
}, 10000);
|
||||
saveErrMsg = err;
|
||||
newport = undefined;
|
||||
});
|
||||
return newport;
|
||||
}
|
||||
|
||||
function createEmptyPort() {
|
||||
if (changes === undefined) return;
|
||||
changes.expand.ports = [...changes.expand.ports, { name: '', number: 1 } as Port];
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if waitingForDevices}
|
||||
<div class="container max-w-lg mx-auto text-center">
|
||||
<span class="loading loading-dots loading-lg" />
|
||||
</div>
|
||||
{:else if device === undefined || changes === undefined}
|
||||
<div class="container max-w-lg mx-auto">
|
||||
<div class="alert alert-error">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="stroke-current shrink-0 h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/></svg
|
||||
>
|
||||
<span><strong>No device with id:</strong> {$page.params.id}</span>
|
||||
<div>
|
||||
<a class="btn btn-sm" href="/">Ok</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<h1 class="text-3xl font-bold mb-8">{device.name}</h1>
|
||||
<div class="card w-full bg-base-300 shadow-xl">
|
||||
<div class="card-body">
|
||||
<form on:submit|preventDefault={saveDevice}>
|
||||
<h2 class="card-title">General</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label" for="device-name">
|
||||
<div class="label-text">
|
||||
<span>Name</span>
|
||||
<span class="text-error">*</span>
|
||||
</div>
|
||||
</label>
|
||||
<input
|
||||
id="device-name"
|
||||
type="text"
|
||||
placeholder="Device name"
|
||||
class="input w-full max-w-xs"
|
||||
bind:value={changes.name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label" for="device-ip">
|
||||
<div class="label-text">
|
||||
<span>IP</span>
|
||||
<span class="text-error">*</span>
|
||||
</div>
|
||||
</label>
|
||||
<input
|
||||
id="device-ip"
|
||||
type="text"
|
||||
placeholder="192.168.0.5"
|
||||
class="input w-full max-w-xs"
|
||||
bind:value={changes.ip}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label" for="device-mac">
|
||||
<div class="label-text">
|
||||
<span>Mac</span>
|
||||
<span class="text-error">*</span>
|
||||
</div>
|
||||
</label>
|
||||
<input
|
||||
id="device-mac"
|
||||
type="text"
|
||||
placeholder="aa:bb:cc:dd:ee:ff"
|
||||
class="input w-full max-w-xs"
|
||||
bind:value={changes.mac}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label" for="device-netmask">
|
||||
<div class="label-text">
|
||||
<span>Netmask</span>
|
||||
<span class="text-error">*</span>
|
||||
</div>
|
||||
</label>
|
||||
<input
|
||||
id="device-netmask"
|
||||
type="text"
|
||||
placeholder="255.255.255.0"
|
||||
class="input w-full max-w-xs"
|
||||
bind:value={changes.netmask}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="card-title mt-8">Ports</h2>
|
||||
<div class="form-control w-full">
|
||||
<p class="my-2">
|
||||
UpSnap can also check if given ports are open. You can define them below.
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
{#each changes.expand.ports as _, index}
|
||||
<DeviceFormPort bind:changes {index} bind:portErrMsg bind:portErrTimeout />
|
||||
{/each}
|
||||
</div>
|
||||
{#if portErrMsg !== ''}
|
||||
<Alert
|
||||
color="error"
|
||||
icon={faTriangleExclamation}
|
||||
message={portErrMsg}
|
||||
customClasses="mt-4 max-w-fit"
|
||||
duration={10000}
|
||||
/>
|
||||
{/if}
|
||||
<button
|
||||
class="btn btn-wide btn-neutral mt-4"
|
||||
on:click={() => createEmptyPort()}
|
||||
type="button">Add new port</button
|
||||
>
|
||||
</div>
|
||||
<h2 class="card-title mt-8">Link</h2>
|
||||
<div class="form-control w-full">
|
||||
<p class="my-2">
|
||||
Makes your device name a clickable link, perfect for linking a dashboard for example.
|
||||
</p>
|
||||
<input
|
||||
type="url"
|
||||
placeholder="https:// ..."
|
||||
class="input w-full max-w-xs"
|
||||
bind:value={changes.link}
|
||||
/>
|
||||
</div>
|
||||
<div class="card-actions justify-end mt-4">
|
||||
<span class="text-error me-4 self-center">* required field</span>
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { pocketbase } from '$lib/stores/pocketbase';
|
||||
import { pocketbase, devices } from '$lib/stores/pocketbase';
|
||||
import Fa from 'svelte-fa';
|
||||
import { faLockOpen, faEye } from '@fortawesome/free-solid-svg-icons';
|
||||
import { toggleVisibility } from '$lib/helpers/forms';
|
||||
import type { Device } from '$lib/types/device';
|
||||
|
||||
let inputPassword: HTMLInputElement;
|
||||
let form = {
|
||||
@@ -17,6 +18,7 @@
|
||||
$pocketbase.admins
|
||||
.authWithPassword(form.email, form.password)
|
||||
.then(() => {
|
||||
getDevices();
|
||||
goto('/');
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -24,6 +26,7 @@
|
||||
.collection('users')
|
||||
.authWithPassword(form.email, form.password)
|
||||
.then(() => {
|
||||
getDevices();
|
||||
goto('/');
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -31,6 +34,15 @@
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getDevices() {
|
||||
$pocketbase
|
||||
.collection('devices')
|
||||
.getFullList(1000, { sort: 'name', expand: 'ports,groups' })
|
||||
.then((data) => {
|
||||
devices.set(data as Device[]);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex h-screen">
|
||||
|
||||
@@ -15,7 +15,7 @@ const config = {
|
||||
fallback: 'index.html'
|
||||
}),
|
||||
prerender: {
|
||||
entries: ['/', '/login', '/welcome']
|
||||
entries: ['/', '/login', '/account', '/welcome', '/device/[id]']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user