feat: sending wake packets, closes #10

This commit is contained in:
axel 2025-04-19 21:54:58 +02:00
parent f1143551d6
commit 96d2696e17
5 changed files with 81 additions and 36 deletions

View File

@ -34,7 +34,7 @@
}} }}
> >
{#if data.Icon} {#if data.Icon}
<data.Icon class="sm:-ml-2 {data.spin ? 'animate-spin' : ''}" /> <data.Icon class="-ml-2 {data.spin ? 'animate-spin' : ''} w-5 sm:w-auto" />
{/if} {/if}
<p role="alert">{data.content}</p> <p role="alert">{data.content}</p>
{#if data.dismissable} {#if data.dismissable}

View File

@ -1,9 +1,10 @@
<script lang="ts"> <script lang="ts">
import { enhance } from '$app/forms';
import { Button } from 'bits-ui'; import { Button } from 'bits-ui';
import IconEdit from '~icons/tabler/edit'; import IconEdit from '~icons/tabler/edit';
import IconPlay from '~icons/tabler/player-play'; import IconPlay from '~icons/tabler/player-play';
let { title, subtitle, editHref = null, wakePost = null } = $props(); let { title, subtitle, editHref = null, wakeId = null } = $props();
</script> </script>
<div <div
@ -25,13 +26,16 @@
</Button.Root> </Button.Root>
{/if} {/if}
{#if wakePost} {#if wakeId}
<form use:enhance method="POST" action="?/wake">
<input type="hidden" name="id" value={wakeId} />
<Button.Root <Button.Root
class="bg-emerald-600 hover:bg-emerald-800 transition-all duration-300 ease-in-out class="bg-emerald-600 hover:bg-emerald-800 transition-all duration-300 ease-in-out
border border-emerald-500 rounded-full p-4 text-neutral-100 cursor-pointer" border border-emerald-500 rounded-full p-4 text-neutral-100 cursor-pointer"
> >
<IconPlay /> <IconPlay />
</Button.Root> </Button.Root>
</form>
{/if} {/if}
</div> </div>
</div> </div>

View File

@ -1,5 +1,7 @@
import { FORBIDDEN, SUCCESS } from '$lib/server/commonResponses';
import { users } from '$lib/server/db'; import { users } from '$lib/server/db';
import type { ServerLoad } from '@sveltejs/kit'; import { fail, type Actions, type ServerLoad } from '@sveltejs/kit';
import { wake } from 'wake_on_lan';
export const load: ServerLoad = async ({ locals: { guard } }) => { export const load: ServerLoad = async ({ locals: { guard } }) => {
const user = guard.requiresAuth().orRedirects().getUser(); const user = guard.requiresAuth().orRedirects().getUser();
@ -7,3 +9,44 @@ export const load: ServerLoad = async ({ locals: { guard } }) => {
devices: await users.fetchDevices(user.id), devices: await users.fetchDevices(user.id),
}; };
}; };
export const actions = {
wake: async ({ locals: { guard }, request }) => {
guard = guard.requiresAuth();
if (guard.isFailed()) return FORBIDDEN;
const deviceId = (await request.formData()).get('id');
const device = (await users.fetchDevices(guard.getUser().id)).find((d) => d.id === deviceId);
if (!device) {
return fail(404, { error: 'Could not find device.' });
}
console.log(
`Sending WOL packets to ${device.name} (${device.mac}) on ${guard.getUser().name}'s request.`,
);
const err = await new Promise<any>((resolve) => {
wake(
device.mac,
{
address: device.broadcast,
port: device.port,
num_packets: device.packets,
},
(err: any) => {
resolve(err);
},
);
});
if (err) {
console.error(err);
return fail(500, {
error: 'An error occured while trying to wake the device. Please see logs.',
});
}
return { ...SUCCESS, deviceName: device.name };
},
} satisfies Actions;

View File

@ -1,11 +1,31 @@
<script> <script>
import { store } from '$lib/v2/globalStore.svelte.js'; import { store } from '$lib/v2/globalStore.svelte.js';
import ListPage from '$lib/v2/snippets/ListPage.svelte'; import ListPage from '$lib/v2/snippets/ListPage.svelte';
import { Toast } from '$lib/v2/toast/notification.js';
import ResCard from '$lib/v2/ui/ResCard.svelte'; import ResCard from '$lib/v2/ui/ResCard.svelte';
import { untrack } from 'svelte';
import IconCheck from '~icons/tabler/check';
import IconX from '~icons/tabler/x';
let { data } = $props(); let { data, form } = $props();
store.pageTitle = 'Listing all devices'; store.pageTitle = 'Listing all devices';
$effect(() => {
if (form?.error && !form.success) {
untrack(() => {
Toast.add({ Icon: IconX, content: form.error, theme: 'error' });
});
} else if (form?.success) {
untrack(() => {
Toast.add({
Icon: IconCheck,
content: `Sent magic packets to ${form.deviceName}.`,
theme: 'success',
});
});
}
});
</script> </script>
<ListPage createHref={data.user.admin ? '/dash/devices/new' : null} msgAdd="Add Device"> <ListPage createHref={data.user.admin ? '/dash/devices/new' : null} msgAdd="Add Device">
@ -15,7 +35,7 @@
title={device.name} title={device.name}
subtitle={device.mac} subtitle={device.mac}
editHref={data.user.admin ? `/dash/devices/${device.id}` : null} editHref={data.user.admin ? `/dash/devices/${device.id}` : null}
wakePost={`/dash/devices/${device.id}/wake`} wakeId={device.id}
/> />
{/each} {/each}
</div> </div>

View File

@ -1,9 +1,8 @@
import { FORBIDDEN, PARSE_ERROR, SUCCESS } from '$lib/server/commonResponses'; import { FORBIDDEN, PARSE_ERROR, SUCCESS } from '$lib/server/commonResponses';
import { devices, users } from '$lib/server/db/index.js'; import { devices } from '$lib/server/db/index.js';
import { fail, redirect, type Actions, type ServerLoad } from '@sveltejs/kit'; import { redirect, type Actions, type ServerLoad } from '@sveltejs/kit';
import { decode } from 'decode-formdata'; import { decode } from 'decode-formdata';
import validator from 'validator'; import validator from 'validator';
import { wake } from 'wake_on_lan';
import { z } from 'zod'; import { z } from 'zod';
export const load: ServerLoad = async ({ locals: { guard }, params }) => { export const load: ServerLoad = async ({ locals: { guard }, params }) => {
@ -65,25 +64,4 @@ export const actions = {
const err = await devices.delete(params.id ?? ''); const err = await devices.delete(params.id ?? '');
return err ? err.toFail() : SUCCESS; return err ? err.toFail() : SUCCESS;
}, },
wake: async ({ params, locals: { guard } }) => {
guard = guard.requiresAuth();
if (guard.isFailed()) return FORBIDDEN;
const device = (await users.fetchDevices(guard.getUser().id)).find((d) => d.id === params.id);
if (!device) {
return fail(404);
}
console.log('Trying to wake ' + device.name);
wake(device.mac, {
address: device.broadcast,
port: device.port,
num_packets: device.packets,
});
return SUCCESS;
},
} satisfies Actions; } satisfies Actions;