aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/routes/(app)/pos/+page.server.ts28
-rw-r--r--src/routes/(app)/pos/+page.svelte13
2 files changed, 40 insertions, 1 deletions
diff --git a/src/routes/(app)/pos/+page.server.ts b/src/routes/(app)/pos/+page.server.ts
index 1df1bb33..1e3e1e99 100644
--- a/src/routes/(app)/pos/+page.server.ts
+++ b/src/routes/(app)/pos/+page.server.ts
@@ -1,6 +1,18 @@
import { PUBLIC_COINOS_URL } from "$env/static/public";
import { auth, fd, get } from "$lib/utils";
import { hex } from "@scure/base";
+import { fail } from "@sveltejs/kit";
+
+const LITTLEFS_SIZE = 0x20000;
+const LITTLEFS_MAGIC = "littlefs";
+
+// A valid image is exactly one partition long and carries the LittleFS
+// superblock magic at byte 8. Anything else (e.g. a JSON error body from
+// the server) must never reach the flasher.
+const isLittleFS = (buf: Uint8Array) =>
+ buf.length === LITTLEFS_SIZE &&
+ new TextDecoder().decode(buf.subarray(8, 8 + LITTLEFS_MAGIC.length)) ===
+ LITTLEFS_MAGIC;
export async function load({ cookies }) {
let token;
@@ -25,6 +37,22 @@ export const actions = {
});
const buf = new Uint8Array(await res.arrayBuffer());
+
+ if (!res.ok) {
+ let message = `Config server returned ${res.status}`;
+ try {
+ message = JSON.parse(new TextDecoder().decode(buf)).message || message;
+ } catch (e) {}
+ return fail(res.status >= 400 && res.status < 600 ? res.status : 500, {
+ error: message,
+ });
+ }
+
+ if (!isLittleFS(buf))
+ return fail(500, {
+ error: `Config server returned an invalid image (${buf.length} bytes)`,
+ });
+
return { bytes: hex.encode(buf) };
},
};
diff --git a/src/routes/(app)/pos/+page.svelte b/src/routes/(app)/pos/+page.svelte
index 871c355d..36869fba 100644
--- a/src/routes/(app)/pos/+page.svelte
+++ b/src/routes/(app)/pos/+page.svelte
@@ -30,7 +30,11 @@
let portInfo = $state("");
// --- Existing config path (generated server-side to hex in form.bytes) ---
- let bytes = $derived(form ? hex.decode(form.bytes) : undefined);
+ let bytes = $derived(form?.bytes ? hex.decode(form.bytes) : undefined);
+ let configError = $state("");
+ $effect(() => {
+ configError = form?.error || "";
+ });
let littlefsAddress = $state(LITTLEFS_ADDRESS_DEFAULT);
let configProgress = $state(0);
let configDone = $state(false);
@@ -68,6 +72,10 @@
let flashConfig = async () => {
if (!esploader || !bytes) return;
+ if (bytes.length !== 0x20000) {
+ configError = `Refusing to flash: image is ${bytes.length} bytes, expected ${0x20000}`;
+ return;
+ }
configDone = false;
configProgress = 0;
@@ -175,6 +183,9 @@
{#if configProgress > 0}<div class="mt-2">Progress: {configProgress}%</div>{/if}
{/if}
{:else}
+ {#if configError}
+ <div class="text-red-600 mb-2">{configError}</div>
+ {/if}
<form method="POST" use:enhance class="space-y-2">
<input name="ssid" placeholder="Wifi SSID" class="input" />
<input name="key" placeholder="Wifi password" class="input" />