From 464905749b4c9b6b3e82203a9f169556ddec7fa1 Mon Sep 17 00:00:00 2001 From: axel Date: Sat, 19 Apr 2025 16:56:41 +0200 Subject: [PATCH] refactor: better form decoding --- package-lock.json | 7 +++++++ package.json | 1 + src/routes/dash/account/+page.server.ts | 3 ++- src/routes/dash/devices/[id]/+page.server.ts | 11 ++--------- src/routes/dash/groups/[id]/+page.server.ts | 12 ++++++------ src/routes/dash/users/[id]/+page.server.ts | 16 +++++++--------- src/routes/login/+page.server.ts | 4 ++-- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 04076e8..52767df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@types/wake_on_lan": "^0.0.33", "bcryptjs": "^3.0.2", "bits-ui": "^1.3.19", + "decode-formdata": "^0.9.0", "drizzle-orm": "^0.41.0", "humanize-duration": "^3.32.1", "lowdb": "^7.0.1", @@ -1700,6 +1701,12 @@ } } }, + "node_modules/decode-formdata": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/decode-formdata/-/decode-formdata-0.9.0.tgz", + "integrity": "sha512-q5uwOjR3Um5YD+ZWPOF/1sGHVW9A5rCrRwITQChRXlmPkxDFBqCm4jNTIVdGHNH9OnR+V9MoZVgRhsFb+ARbUw==", + "license": "MIT" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", diff --git a/package.json b/package.json index da69b9f..6e58d7d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@types/wake_on_lan": "^0.0.33", "bcryptjs": "^3.0.2", "bits-ui": "^1.3.19", + "decode-formdata": "^0.9.0", "drizzle-orm": "^0.41.0", "humanize-duration": "^3.32.1", "lowdb": "^7.0.1", diff --git a/src/routes/dash/account/+page.server.ts b/src/routes/dash/account/+page.server.ts index 023a772..518d790 100644 --- a/src/routes/dash/account/+page.server.ts +++ b/src/routes/dash/account/+page.server.ts @@ -4,6 +4,7 @@ import type { Updated } from '$lib/server/db/types'; import { toPublicUser, type User } from '$lib/server/db/types/user.js'; import { type Actions, type ServerLoad } from '@sveltejs/kit'; import bcrypt from 'bcryptjs'; +import { decode } from 'decode-formdata'; import { z } from 'zod'; export const load: ServerLoad = async ({ locals: { guard }, params }) => { @@ -31,7 +32,7 @@ export const actions = { }), }); - const parsed = schema.safeParse(Object.fromEntries(await request.formData())); + const parsed = schema.safeParse(decode(await request.formData())); if (!parsed.success) return PARSE_ERROR(parsed.error); let updatedUser: Updated = { id: user.id, ...parsed.data }; diff --git a/src/routes/dash/devices/[id]/+page.server.ts b/src/routes/dash/devices/[id]/+page.server.ts index 12821d7..894fd6e 100644 --- a/src/routes/dash/devices/[id]/+page.server.ts +++ b/src/routes/dash/devices/[id]/+page.server.ts @@ -1,6 +1,7 @@ import { FORBIDDEN, PARSE_ERROR, SUCCESS } from '$lib/server/commonResponses'; import { devices, users } from '$lib/server/db/index.js'; import { fail, redirect, type Actions, type ServerLoad } from '@sveltejs/kit'; +import { decode } from 'decode-formdata'; import validator from 'validator'; import { wake } from 'wake_on_lan'; import { z } from 'zod'; @@ -23,8 +24,6 @@ export const actions = { update: async ({ request, params, locals: { guard } }) => { if (guard.requiresAdmin().isFailed()) return FORBIDDEN; - const form = await request.formData(); - const schema = z.object({ name: z .string({ message: 'Name is required.' }) @@ -46,13 +45,7 @@ export const actions = { .max(50, { message: 'Packets quantity must be at most 50.' }), }); - const parsed = schema.safeParse({ - name: form.get('name'), - mac: form.get('mac'), - broadcast: form.get('broadcast'), - port: form.get('port'), - packets: form.get('packets'), - }); + const parsed = schema.safeParse(decode(await request.formData())); if (!parsed.success) { return PARSE_ERROR(parsed.error); diff --git a/src/routes/dash/groups/[id]/+page.server.ts b/src/routes/dash/groups/[id]/+page.server.ts index a429b9f..c0587e6 100644 --- a/src/routes/dash/groups/[id]/+page.server.ts +++ b/src/routes/dash/groups/[id]/+page.server.ts @@ -1,6 +1,7 @@ import { FORBIDDEN, PARSE_ERROR, SUCCESS } from '$lib/server/commonResponses'; import { devices, groups } from '$lib/server/db'; import { redirect, type Actions, type ServerLoad } from '@sveltejs/kit'; +import { decode } from 'decode-formdata'; import { z } from 'zod'; export const load: ServerLoad = async ({ locals: { guard }, params }) => { @@ -22,8 +23,6 @@ export const actions = { update: async ({ request, locals: { guard }, params }) => { if (guard.requiresAdmin().isFailed()) return FORBIDDEN; - const form = await request.formData(); - const schema = z.object({ name: z .string({ message: 'Name is required.' }) @@ -32,10 +31,11 @@ export const actions = { devices: z.array(z.string()), }); - const parsed = schema.safeParse({ - name: form.get('name'), - devices: form.getAll('devices'), - }); + const parsed = schema.safeParse( + decode(await request.formData(), { + arrays: ['devices'], + }), + ); if (!parsed.success) { return PARSE_ERROR(parsed.error); diff --git a/src/routes/dash/users/[id]/+page.server.ts b/src/routes/dash/users/[id]/+page.server.ts index b9237dc..afb7f51 100644 --- a/src/routes/dash/users/[id]/+page.server.ts +++ b/src/routes/dash/users/[id]/+page.server.ts @@ -4,6 +4,7 @@ import type { Updated } from '$lib/server/db/types'; import { toPublicUser, type User } from '$lib/server/db/types/user.js'; import { redirect, type Actions, type ServerLoad } from '@sveltejs/kit'; import bcrypt from 'bcryptjs'; +import { decode } from 'decode-formdata'; import { z } from 'zod'; export const load: ServerLoad = async ({ locals: { guard }, params }) => { @@ -26,8 +27,6 @@ export const actions = { update: async ({ request, locals: { guard }, params }) => { if (guard.requiresAdmin().isFailed()) return FORBIDDEN; - const form = await request.formData(); - const schema = z.object({ name: z .string({ message: 'Name is required.' }) @@ -48,13 +47,12 @@ export const actions = { devices: z.array(z.string()), }); - const parsed = schema.safeParse({ - name: form.get('name'), - admin: form.get('admin') === 'on', - password: form.get('password'), - groups: form.getAll('groups'), - devices: form.getAll('devices'), - }); + const parsed = schema.safeParse( + decode(await request.formData(), { + arrays: ['groups', 'devices'], + booleans: ['admin'], + }), + ); if (!parsed.success) { return PARSE_ERROR(parsed.error); diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts index 9fd3d7a..a693319 100644 --- a/src/routes/login/+page.server.ts +++ b/src/routes/login/+page.server.ts @@ -4,6 +4,7 @@ import { users } from '$lib/server/db'; import { createSession } from '$lib/server/sessions'; import { fail, redirect } from '@sveltejs/kit'; import bcrypt from 'bcryptjs'; +import { decode } from 'decode-formdata'; import humanizeDuration from 'humanize-duration'; import { z } from 'zod'; import type { Actions } from './$types'; @@ -60,8 +61,7 @@ export const actions = { password: z.string({ message: 'Password is required.' }), }); - const data = await request.formData(); - const parsed = schema.safeParse(Object.fromEntries(data.entries())); + const parsed = schema.safeParse(decode(await request.formData())); if (!parsed.success) { return PARSE_ERROR(parsed.error);