114 lines
2.9 KiB
TypeScript
114 lines
2.9 KiB
TypeScript
import { db } from '$lib/server/db';
|
|
import type { User } from '$lib/server/db/types/user.js';
|
|
import { fail, redirect, type Actions } from '@sveltejs/kit';
|
|
import bcrypt from 'bcryptjs';
|
|
import { nanoid } from 'nanoid';
|
|
import { z } from 'zod';
|
|
|
|
export const load = async ({ locals: { guard }, params }) => {
|
|
guard.requiresAdmin().orRedirects();
|
|
|
|
let user = db.data.users.find((u) => u.id === params.slug) as Partial<User>;
|
|
|
|
if (!user && params.slug !== 'new') {
|
|
redirect(302, '/dashboard/users');
|
|
}
|
|
|
|
if (user) {
|
|
user = structuredClone(user);
|
|
delete user['password'];
|
|
}
|
|
|
|
return {
|
|
user,
|
|
groups: db.data.groups,
|
|
devices: db.data.devices,
|
|
};
|
|
};
|
|
|
|
export const actions: Actions = {
|
|
update: async ({ request, locals: { guard }, params }) => {
|
|
if (guard.requiresAdmin().isFailed()) return fail(403);
|
|
|
|
const form = await request.formData();
|
|
|
|
const schema = z.object({
|
|
name: z
|
|
.string({ message: 'Name is required.' })
|
|
.min(3, { message: 'Name must be at least 3 characters.' })
|
|
.max(24, { message: 'Name must be at most 24 characters.' }),
|
|
admin: z.boolean(),
|
|
password: z
|
|
.string()
|
|
.optional()
|
|
.refine((v) => (params.slug === 'new' ? v && v.length > 0 : true), {
|
|
message: 'Password is required at user creation.',
|
|
})
|
|
.refine((v) => !v || v.length >= 8, {
|
|
message: 'Password must be at least 8 characters.',
|
|
}),
|
|
groups: z.array(
|
|
z.string().refine((v) => db.data.groups.find((g) => g.id === v), {
|
|
message: 'Invalid group ID.',
|
|
}),
|
|
),
|
|
devices: z.array(
|
|
z.string().refine((v) => db.data.devices.find((d) => d.id === v), {
|
|
message: 'Invalid device ID.',
|
|
}),
|
|
),
|
|
});
|
|
|
|
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'),
|
|
});
|
|
|
|
if (!parsed.success) {
|
|
return fail(400, { error: parsed.error.errors[0].message });
|
|
}
|
|
|
|
if (params.slug === 'new') {
|
|
await db.update(({ users }) => {
|
|
users.push({
|
|
id: nanoid(),
|
|
name: parsed.data.name,
|
|
admin: parsed.data.admin,
|
|
groups: parsed.data.groups,
|
|
devices: parsed.data.devices,
|
|
password: bcrypt.hashSync(parsed.data.password!, 10),
|
|
});
|
|
});
|
|
|
|
redirect(302, '/dashboard/users');
|
|
}
|
|
|
|
await db.update(({ users }) => {
|
|
let user = users.find((u) => u.id === params.slug);
|
|
if (!user) return;
|
|
|
|
user.name = parsed.data.name;
|
|
user.admin = parsed.data.admin;
|
|
user.groups = parsed.data.groups;
|
|
user.devices = parsed.data.devices;
|
|
|
|
if (parsed.data.password && parsed.data.password.length > 0) {
|
|
user.password = bcrypt.hashSync(parsed.data.password, 10);
|
|
}
|
|
});
|
|
|
|
redirect(302, '/dashboard/users');
|
|
},
|
|
delete: async ({ locals: { guard }, params }) => {
|
|
if (guard.requiresAdmin().isFailed()) {
|
|
return fail(403);
|
|
}
|
|
|
|
db.data.users = db.data.users.filter((u) => u.id !== params.slug);
|
|
db.write();
|
|
},
|
|
};
|