From 298188722493baf1cbc0f6209fdc3360ea137a6c Mon Sep 17 00:00:00 2001 From: axel Date: Thu, 17 Apr 2025 03:26:15 +0200 Subject: [PATCH] style: fixed indent -> everything into 2 spaces --- .prettierrc | 2 +- src/app.css | 4 +- src/app.d.ts | 20 +- src/app.html | 18 +- src/hooks.server.ts | 34 +-- src/lib/server/db/index.ts | 8 +- src/lib/server/db/repos/deviceRepo.ts | 10 +- src/lib/server/db/repos/groupRepo.ts | 12 +- src/lib/server/db/repos/lowdb/_init.ts | 34 +-- src/lib/server/db/repos/lowdb/deviceRepo.ts | 108 ++++----- src/lib/server/db/repos/lowdb/groupRepo.ts | 124 +++++----- src/lib/server/db/repos/lowdb/userRepo.ts | 162 ++++++------- src/lib/server/db/repos/userRepo.ts | 16 +- src/lib/server/db/types/device.ts | 26 +- src/lib/server/db/types/group.ts | 28 +-- src/lib/server/db/types/index.ts | 46 ++-- src/lib/server/db/types/user.ts | 48 ++-- src/lib/server/guard.ts | 70 +++--- src/lib/server/sessions.ts | 24 +- src/lib/v2/forms/InputCheckbox.svelte | 56 ++--- src/lib/v2/forms/InputCombobox.svelte | 242 +++++++++---------- src/lib/v2/forms/InputText.svelte | 42 ++-- src/lib/v2/snippets/EditPage.svelte | 74 +++--- src/lib/v2/snippets/ListPage.svelte | 40 +-- src/lib/v2/transitions/slideFade.ts | 58 ++--- src/lib/v2/ui/Collapsible.svelte | 62 ++--- src/lib/v2/ui/NavBar.svelte | 14 +- src/lib/v2/ui/NavBarLink.svelte | 28 +-- src/lib/v2/ui/ResCard.svelte | 52 ++-- src/routes/+layout.svelte | 4 +- src/routes/+page.server.ts | 2 +- src/routes/dash/+layout.server.ts | 8 +- src/routes/dash/+layout.svelte | 158 ++++++------ src/routes/dash/+page.server.ts | 4 +- src/routes/dash/devices/+page.server.ts | 8 +- src/routes/dash/devices/+page.svelte | 30 +-- src/routes/dash/devices/[id]/+page.server.ts | 134 +++++----- src/routes/dash/devices/[id]/+page.svelte | 120 ++++----- src/routes/dash/groups/+page.server.ts | 20 +- src/routes/dash/groups/+page.svelte | 28 +-- src/routes/dash/groups/[id]/+page.server.ts | 74 +++--- src/routes/dash/groups/[id]/+page.svelte | 64 ++--- src/routes/dash/users/+page.server.ts | 10 +- src/routes/dash/users/+page.svelte | 28 +-- src/routes/dash/users/[id]/+page.server.ts | 128 +++++----- src/routes/dash/users/[id]/+page.svelte | 118 ++++----- src/routes/login/+page.server.ts | 70 +++--- src/routes/login/+page.svelte | 26 +- src/routes/logout/+page.server.ts | 12 +- 49 files changed, 1255 insertions(+), 1253 deletions(-) diff --git a/.prettierrc b/.prettierrc index 9cfdeb9..33afac8 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,5 @@ { - "useTabs": true, + "useTabs": false, "singleQuote": true, "trailingComma": "all", "printWidth": 100, diff --git a/src/app.css b/src/app.css index 736ecf1..adc8a5c 100644 --- a/src/app.css +++ b/src/app.css @@ -2,5 +2,5 @@ @import 'tailwindcss'; * { - font-family: "Inter", sans-serif; -} \ No newline at end of file + font-family: 'Inter', sans-serif; +} diff --git a/src/app.d.ts b/src/app.d.ts index 693aaee..0dcd3b8 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,19 +1,19 @@ // See https://svelte.dev/docs/kit/types#app.d.ts -import type { Guard } from "$lib/server/guard"; +import type { Guard } from '$lib/server/guard'; import 'unplugin-icons/types/svelte'; // for information about these interfaces declare global { - namespace App { - // interface Error {} - interface Locals { - guard: Guard; - } - // interface PageData {} - // interface PageState {} - // interface Platform {} - } + namespace App { + // interface Error {} + interface Locals { + guard: Guard; + } + // interface PageData {} + // interface PageState {} + // interface Platform {} + } } export {}; diff --git a/src/app.html b/src/app.html index 5889edb..25525f6 100644 --- a/src/app.html +++ b/src/app.html @@ -1,12 +1,12 @@ - - - - - %sveltekit.head% - - -
%sveltekit.body%
- + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 9f5c4bc..b46c511 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -7,30 +7,30 @@ import { writeFileSync } from 'fs'; import { nanoid } from 'nanoid'; export const init: ServerInit = async () => { - await initRepos(); + await initRepos(); - if (!(await users.any())) { - const pass = nanoid(); + if (!(await users.any())) { + const pass = nanoid(); - await users.create({ - name: 'admin', - password: bcrypt.hashSync(pass, 10), - admin: true, - groups: [], - devices: [], - }); + await users.create({ + name: 'admin', + password: bcrypt.hashSync(pass, 10), + admin: true, + groups: [], + devices: [], + }); - console.log(`default admin password: ${pass}`); - console.log("saved to ./default_admin_pass.txt, don't share it and change it asap"); + console.log(`default admin password: ${pass}`); + console.log("saved to ./default_admin_pass.txt, don't share it and change it asap"); - writeFileSync('./data/default_admin_pass.txt', pass); - } + writeFileSync('./data/default_admin_pass.txt', pass); + } }; export const handle: Handle = async ({ event, resolve }) => { - const { cookies, locals } = event; + const { cookies, locals } = event; - locals.guard = new Guard(await getUserFromSession(cookies.get('session'))); + locals.guard = new Guard(await getUserFromSession(cookies.get('session'))); - return await resolve(event); + return await resolve(event); }; diff --git a/src/lib/server/db/index.ts b/src/lib/server/db/index.ts index d99314e..1cceaeb 100644 --- a/src/lib/server/db/index.ts +++ b/src/lib/server/db/index.ts @@ -8,9 +8,9 @@ export let groups: IGroupRepo; export let devices: IDeviceRepo; export async function initRepos() { - const repos = await LowStorage.init(); + const repos = await LowStorage.init(); - users = repos.users; - groups = repos.groups; - devices = repos.devices; + users = repos.users; + groups = repos.groups; + devices = repos.devices; } diff --git a/src/lib/server/db/repos/deviceRepo.ts b/src/lib/server/db/repos/deviceRepo.ts index 8c4e583..56f4f2d 100644 --- a/src/lib/server/db/repos/deviceRepo.ts +++ b/src/lib/server/db/repos/deviceRepo.ts @@ -2,9 +2,9 @@ import type { New, Updated } from '../types'; import type { Device, DeviceError } from '../types/device'; export interface IDeviceRepo { - getAll(): Promise; - getById(id: string): Promise; - create(group: New): Promise; - update(group: Updated): Promise; - delete(groupId: string): Promise; + getAll(): Promise; + getById(id: string): Promise; + create(group: New): Promise; + update(group: Updated): Promise; + delete(groupId: string): Promise; } diff --git a/src/lib/server/db/repos/groupRepo.ts b/src/lib/server/db/repos/groupRepo.ts index 28b09be..2580141 100644 --- a/src/lib/server/db/repos/groupRepo.ts +++ b/src/lib/server/db/repos/groupRepo.ts @@ -2,10 +2,10 @@ import type { New, Updated } from '../types'; import type { Group, GroupError } from '../types/group'; export interface IGroupRepo { - getAll(): Promise; - getById(id: string): Promise; - create(group: New): Promise; - update(group: Updated): Promise; - delete(groupId: string): Promise; - countUsers(groupId: string): Promise; + getAll(): Promise; + getById(id: string): Promise; + create(group: New): Promise; + update(group: Updated): Promise; + delete(groupId: string): Promise; + countUsers(groupId: string): Promise; } diff --git a/src/lib/server/db/repos/lowdb/_init.ts b/src/lib/server/db/repos/lowdb/_init.ts index 2fb17c5..fe79089 100644 --- a/src/lib/server/db/repos/lowdb/_init.ts +++ b/src/lib/server/db/repos/lowdb/_init.ts @@ -2,28 +2,28 @@ import { JSONFilePreset } from 'lowdb/node'; import type { Device } from '../../types/device'; import type { Group } from '../../types/group'; import type { User } from '../../types/user'; -import { LowUserRepo } from './userRepo'; -import { LowGroupRepo } from './groupRepo'; import { LowDeviceRepo } from './deviceRepo'; +import { LowGroupRepo } from './groupRepo'; +import { LowUserRepo } from './userRepo'; const db = await JSONFilePreset('./data/db.json', { - users: [], - groups: [], - devices: [], + users: [], + groups: [], + devices: [], }); export namespace LowStorage { - export type LowData = { - users: User[]; - groups: Group[]; - devices: Device[]; - }; + export type LowData = { + users: User[]; + groups: Group[]; + devices: Device[]; + }; - export async function init() { - return { - users: new LowUserRepo(db), - groups: new LowGroupRepo(db), - devices: new LowDeviceRepo(db), - }; - } + export async function init() { + return { + users: new LowUserRepo(db), + groups: new LowGroupRepo(db), + devices: new LowDeviceRepo(db), + }; + } } diff --git a/src/lib/server/db/repos/lowdb/deviceRepo.ts b/src/lib/server/db/repos/lowdb/deviceRepo.ts index 42fea2e..68acf5e 100644 --- a/src/lib/server/db/repos/lowdb/deviceRepo.ts +++ b/src/lib/server/db/repos/lowdb/deviceRepo.ts @@ -1,76 +1,76 @@ -import type { IDeviceRepo } from '../deviceRepo'; import type { Low } from 'lowdb'; +import { nanoid } from 'nanoid'; import { type New, type Updated } from '../../types'; import { type Device, type DeviceError, DeviceErrors } from '../../types/device'; -import { nanoid } from 'nanoid'; +import type { IDeviceRepo } from '../deviceRepo'; import type { LowStorage } from './_init'; export class LowDeviceRepo implements IDeviceRepo { - private readonly db; + private readonly db; - constructor(db: Low) { - this.db = db; - } + constructor(db: Low) { + this.db = db; + } - async getAll(): Promise { - return this.db.data.devices; - } + async getAll(): Promise { + return this.db.data.devices; + } - async getById(id: string): Promise { - return this.db.data.devices.find((d) => d.id === id); - } + async getById(id: string): Promise { + return this.db.data.devices.find((d) => d.id === id); + } - async create(device: New): Promise { - if (this.db.data.devices.find((d) => d.name === device.name)) { - return DeviceErrors.DUPLICATE_NAME; - } + async create(device: New): Promise { + if (this.db.data.devices.find((d) => d.name === device.name)) { + return DeviceErrors.DUPLICATE_NAME; + } - await this.db.update(({ devices }) => { - devices.push({ id: nanoid(), ...device }); - }); + await this.db.update(({ devices }) => { + devices.push({ id: nanoid(), ...device }); + }); - return undefined; - } + return undefined; + } - async update(device: Updated): Promise { - if (this.db.data.devices.find((d) => d.name === device.name && d.id !== device.id)) { - return DeviceErrors.DUPLICATE_NAME; - } + async update(device: Updated): Promise { + if (this.db.data.devices.find((d) => d.name === device.name && d.id !== device.id)) { + return DeviceErrors.DUPLICATE_NAME; + } - let found = false; - await this.db.update(({ devices }) => { - const existingDevice = devices.find((d) => d.id === device.id); - if (!existingDevice) return; + let found = false; + await this.db.update(({ devices }) => { + const existingDevice = devices.find((d) => d.id === device.id); + if (!existingDevice) return; - Object.assign(existingDevice, device); - found = true; - }); + Object.assign(existingDevice, device); + found = true; + }); - return found ? undefined : DeviceErrors.NOT_FOUND; - } + return found ? undefined : DeviceErrors.NOT_FOUND; + } - async delete(deviceId: string): Promise { - let found = false; + async delete(deviceId: string): Promise { + let found = false; - this.db.data.devices = this.db.data.devices.filter((d) => { - if (d.id === deviceId) { - found = true; - return false; - } - return true; - }); + this.db.data.devices = this.db.data.devices.filter((d) => { + if (d.id === deviceId) { + found = true; + return false; + } + return true; + }); - if (found) { - this.db.data.users.forEach((u) => { - u.devices = u.devices.filter((d) => d !== deviceId); - }); - this.db.data.groups.forEach((g) => { - g.devices = g.devices.filter((d) => d !== deviceId); - }); + if (found) { + this.db.data.users.forEach((u) => { + u.devices = u.devices.filter((d) => d !== deviceId); + }); + this.db.data.groups.forEach((g) => { + g.devices = g.devices.filter((d) => d !== deviceId); + }); - await this.db.write(); - } + await this.db.write(); + } - return found ? undefined : DeviceErrors.NOT_FOUND; - } + return found ? undefined : DeviceErrors.NOT_FOUND; + } } diff --git a/src/lib/server/db/repos/lowdb/groupRepo.ts b/src/lib/server/db/repos/lowdb/groupRepo.ts index 417072d..9288c0d 100644 --- a/src/lib/server/db/repos/lowdb/groupRepo.ts +++ b/src/lib/server/db/repos/lowdb/groupRepo.ts @@ -1,86 +1,86 @@ -import type { IGroupRepo } from '../groupRepo'; import type { Low } from 'lowdb'; +import { nanoid } from 'nanoid'; import { type New, type Updated } from '../../types'; import { type Group, type GroupError, GroupErrors } from '../../types/group'; -import { nanoid } from 'nanoid'; +import type { IGroupRepo } from '../groupRepo'; import type { LowStorage } from './_init'; export class LowGroupRepo implements IGroupRepo { - private readonly db; + private readonly db; - constructor(db: Low) { - this.db = db; - } + constructor(db: Low) { + this.db = db; + } - async getAll(): Promise { - return this.db.data.groups; - } + async getAll(): Promise { + return this.db.data.groups; + } - async getById(id: string): Promise { - return this.db.data.groups.find((g) => g.id === id); - } + async getById(id: string): Promise { + return this.db.data.groups.find((g) => g.id === id); + } - async create(group: New): Promise { - if (this.db.data.groups.find((g) => g.name === group.name)) { - return GroupErrors.DUPLICATE_NAME; - } + async create(group: New): Promise { + if (this.db.data.groups.find((g) => g.name === group.name)) { + return GroupErrors.DUPLICATE_NAME; + } - for (const deviceId of group.devices) { - if (!this.db.data.devices.some((d) => d.id === deviceId)) { - return GroupErrors.UNKNOWN_DEVICE; - } - } + for (const deviceId of group.devices) { + if (!this.db.data.devices.some((d) => d.id === deviceId)) { + return GroupErrors.UNKNOWN_DEVICE; + } + } - await this.db.update(({ groups }) => { - groups.push({ id: nanoid(), ...group }); - }); + await this.db.update(({ groups }) => { + groups.push({ id: nanoid(), ...group }); + }); - return undefined; - } + return undefined; + } - async update(group: Updated): Promise { - if (this.db.data.groups.find((g) => g.name === group.name && g.id !== group.id)) { - return GroupErrors.DUPLICATE_NAME; - } + async update(group: Updated): Promise { + if (this.db.data.groups.find((g) => g.name === group.name && g.id !== group.id)) { + return GroupErrors.DUPLICATE_NAME; + } - for (const deviceId of group.devices ?? []) { - if (!this.db.data.devices.some((d) => d.id === deviceId)) { - return GroupErrors.UNKNOWN_DEVICE; - } - } + for (const deviceId of group.devices ?? []) { + if (!this.db.data.devices.some((d) => d.id === deviceId)) { + return GroupErrors.UNKNOWN_DEVICE; + } + } - let found = false; - await this.db.update(({ groups }) => { - const existingGroup = groups.find((g) => g.id === group.id); - if (!existingGroup) return; + let found = false; + await this.db.update(({ groups }) => { + const existingGroup = groups.find((g) => g.id === group.id); + if (!existingGroup) return; - Object.assign(existingGroup, group); - found = true; - }); + Object.assign(existingGroup, group); + found = true; + }); - return found ? undefined : GroupErrors.NOT_FOUND; - } + return found ? undefined : GroupErrors.NOT_FOUND; + } - async delete(groupId: string): Promise { - let found = false; + async delete(groupId: string): Promise { + let found = false; - this.db.data.groups = this.db.data.groups.filter((g) => { - if (g.id === groupId) { - found = true; - return false; - } - return true; - }); + this.db.data.groups = this.db.data.groups.filter((g) => { + if (g.id === groupId) { + found = true; + return false; + } + return true; + }); - if (found) { - this.db.data.users.forEach((u) => (u.groups = u.groups.filter((gid) => gid != groupId))); - await this.db.write(); - } + if (found) { + this.db.data.users.forEach((u) => (u.groups = u.groups.filter((gid) => gid != groupId))); + await this.db.write(); + } - return found ? undefined : GroupErrors.NOT_FOUND; - } + return found ? undefined : GroupErrors.NOT_FOUND; + } - async countUsers(groupId: string): Promise { - return this.db.data.users.filter((u) => u.groups.includes(groupId)).length; - } + async countUsers(groupId: string): Promise { + return this.db.data.users.filter((u) => u.groups.includes(groupId)).length; + } } diff --git a/src/lib/server/db/repos/lowdb/userRepo.ts b/src/lib/server/db/repos/lowdb/userRepo.ts index 808ab72..426c927 100644 --- a/src/lib/server/db/repos/lowdb/userRepo.ts +++ b/src/lib/server/db/repos/lowdb/userRepo.ts @@ -1,113 +1,113 @@ import type { Low } from 'lowdb'; +import { nanoid } from 'nanoid'; import { type New, type Updated } from '../../types'; import { UserErrors, type User } from '../../types/user'; import type { IUserRepo } from '../userRepo'; -import { nanoid } from 'nanoid'; import type { LowStorage } from './_init'; export class LowUserRepo implements IUserRepo { - private readonly db; + private readonly db; - constructor(db: Low) { - this.db = db; - } + constructor(db: Low) { + this.db = db; + } - async any() { - return this.db.data.users[0] != undefined; - } + async any() { + return this.db.data.users[0] != undefined; + } - async getAll() { - return this.db.data.users; - } + async getAll() { + return this.db.data.users; + } - async getById(id: string) { - return this.db.data.users.find((u) => u.id == id); - } + async getById(id: string) { + return this.db.data.users.find((u) => u.id == id); + } - async getByName(name: string) { - return this.db.data.users.find((u) => u.name == name); - } + async getByName(name: string) { + return this.db.data.users.find((u) => u.name == name); + } - async create(user: New) { - if (!this.db.data.users.find((u) => u.name == user.name)) { - return UserErrors.DUPLICATE_NAME; - } + async create(user: New) { + if (!this.db.data.users.find((u) => u.name == user.name)) { + return UserErrors.DUPLICATE_NAME; + } - for (let id of user.devices) { - if (!this.db.data.devices.some((d) => d.id == id)) { - return UserErrors.UNKNOWN_DEVICE; - } - } + for (let id of user.devices) { + if (!this.db.data.devices.some((d) => d.id == id)) { + return UserErrors.UNKNOWN_DEVICE; + } + } - for (let id of user.groups) { - if (!this.db.data.groups.some((g) => g.id == id)) { - return UserErrors.UNKNOWN_GROUP; - } - } + for (let id of user.groups) { + if (!this.db.data.groups.some((g) => g.id == id)) { + return UserErrors.UNKNOWN_GROUP; + } + } - await this.db.update(({ users }) => { - users.push({ id: nanoid(), ...user }); - }); - } + await this.db.update(({ users }) => { + users.push({ id: nanoid(), ...user }); + }); + } - async update(user: Updated) { - if (this.db.data.users.find((u) => u.name == user.name && u.id != user.id)) { - return UserErrors.DUPLICATE_NAME; - } + async update(user: Updated) { + if (this.db.data.users.find((u) => u.name == user.name && u.id != user.id)) { + return UserErrors.DUPLICATE_NAME; + } - for (let id of user.devices ?? []) { - if (!this.db.data.devices.some((d) => d.id == id)) { - return UserErrors.UNKNOWN_DEVICE; - } - } + for (let id of user.devices ?? []) { + if (!this.db.data.devices.some((d) => d.id == id)) { + return UserErrors.UNKNOWN_DEVICE; + } + } - for (let id of user.groups ?? []) { - if (!this.db.data.groups.some((g) => g.id == id)) { - return UserErrors.UNKNOWN_GROUP; - } - } + for (let id of user.groups ?? []) { + if (!this.db.data.groups.some((g) => g.id == id)) { + return UserErrors.UNKNOWN_GROUP; + } + } - let found = false; - await this.db.update(({ users }) => { - let existing = users.find((u) => u.id == user.id); - if (!existing) return; + let found = false; + await this.db.update(({ users }) => { + let existing = users.find((u) => u.id == user.id); + if (!existing) return; - Object.assign(existing, user); - found = true; - }); + Object.assign(existing, user); + found = true; + }); - return found ? undefined : UserErrors.NOT_FOUND; - } + return found ? undefined : UserErrors.NOT_FOUND; + } - async fetchDevices(userId: string) { - const user = this.db.data.users.find((u) => u.id === userId)!; + async fetchDevices(userId: string) { + const user = this.db.data.users.find((u) => u.id === userId)!; - if (user.admin) return this.db.data.devices; + if (user.admin) return this.db.data.devices; - const userDevices = this.db.data.devices.filter((d) => user.devices.includes(d.id)); - const userGroups = this.db.data.groups.filter((g) => user.groups.includes(g.id)); - const groupDevices = userGroups.flatMap((g) => - this.db.data.devices.filter((d) => g.devices.includes(d.id)), - ); + const userDevices = this.db.data.devices.filter((d) => user.devices.includes(d.id)); + const userGroups = this.db.data.groups.filter((g) => user.groups.includes(g.id)); + const groupDevices = userGroups.flatMap((g) => + this.db.data.devices.filter((d) => g.devices.includes(d.id)), + ); - // concat and dedupe - return [...new Set([...userDevices, ...groupDevices])]; - } + // concat and dedupe + return [...new Set([...userDevices, ...groupDevices])]; + } - async delete(userId: string) { - let found = false; + async delete(userId: string) { + let found = false; - this.db.data.users = this.db.data.users.filter((u) => { - if (u.id == userId) { - found = true; - return false; - } + this.db.data.users = this.db.data.users.filter((u) => { + if (u.id == userId) { + found = true; + return false; + } - return true; - }); + return true; + }); - await this.db.write(); + await this.db.write(); - return found ? undefined : UserErrors.NOT_FOUND; - } + return found ? undefined : UserErrors.NOT_FOUND; + } } diff --git a/src/lib/server/db/repos/userRepo.ts b/src/lib/server/db/repos/userRepo.ts index 6ddbefa..d24b0c4 100644 --- a/src/lib/server/db/repos/userRepo.ts +++ b/src/lib/server/db/repos/userRepo.ts @@ -3,12 +3,12 @@ import type { Device } from '../types/device'; import type { User, UserError } from '../types/user'; export interface IUserRepo { - any(): Promise; - getAll(): Promise; - getById(id: string): Promise; - getByName(name: string): Promise; - fetchDevices(userId: string): Promise; - create(user: New): Promise; - update(user: Updated): Promise; - delete(userId: string): Promise; + any(): Promise; + getAll(): Promise; + getById(id: string): Promise; + getByName(name: string): Promise; + fetchDevices(userId: string): Promise; + create(user: New): Promise; + update(user: Updated): Promise; + delete(userId: string): Promise; } diff --git a/src/lib/server/db/types/device.ts b/src/lib/server/db/types/device.ts index 6645ea6..3dc965f 100644 --- a/src/lib/server/db/types/device.ts +++ b/src/lib/server/db/types/device.ts @@ -1,23 +1,23 @@ import { AppError } from '.'; export type Device = { - id: string; - name: string; - mac: string; - broadcast: string; - port: number; - packets: number; + id: string; + name: string; + mac: string; + broadcast: string; + port: number; + packets: number; }; export class DeviceError extends AppError { - constructor(message: string, status: number) { - super(message, status); - this.name = 'DeviceError'; - Object.setPrototypeOf(this, DeviceError.prototype); - } + constructor(message: string, status: number) { + super(message, status); + this.name = 'DeviceError'; + Object.setPrototypeOf(this, DeviceError.prototype); + } } export namespace DeviceErrors { - export const NOT_FOUND = new DeviceError('Group not found.', 404); - export const DUPLICATE_NAME = new DeviceError('Device with this name already exists.', 409); + export const NOT_FOUND = new DeviceError('Group not found.', 404); + export const DUPLICATE_NAME = new DeviceError('Device with this name already exists.', 409); } diff --git a/src/lib/server/db/types/group.ts b/src/lib/server/db/types/group.ts index c04b0ca..f699ecb 100644 --- a/src/lib/server/db/types/group.ts +++ b/src/lib/server/db/types/group.ts @@ -1,24 +1,24 @@ import { AppError } from '.'; export type Group = { - id: string; - name: string; - devices: string[]; + id: string; + name: string; + devices: string[]; }; export class GroupError extends AppError { - constructor(message: string, status: number) { - super(message, status); - this.name = 'GroupError'; - Object.setPrototypeOf(this, GroupError.prototype); - } + constructor(message: string, status: number) { + super(message, status); + this.name = 'GroupError'; + Object.setPrototypeOf(this, GroupError.prototype); + } } export namespace GroupErrors { - export const NOT_FOUND = new GroupError('Group not found.', 404); - export const DUPLICATE_NAME = new GroupError('Group with this name already exists.', 409); - export const UNKNOWN_DEVICE = new GroupError( - 'One or more of the selected devices do not exist.', - 400, - ); + export const NOT_FOUND = new GroupError('Group not found.', 404); + export const DUPLICATE_NAME = new GroupError('Group with this name already exists.', 409); + export const UNKNOWN_DEVICE = new GroupError( + 'One or more of the selected devices do not exist.', + 400, + ); } diff --git a/src/lib/server/db/types/index.ts b/src/lib/server/db/types/index.ts index 0c2c143..cf3cbf0 100644 --- a/src/lib/server/db/types/index.ts +++ b/src/lib/server/db/types/index.ts @@ -2,36 +2,36 @@ import { fail } from '@sveltejs/kit'; /** @deprecated unused for now - keeping around just in case but should be removed someday */ export class Result { - public readonly err; - private readonly res; + public readonly err; + private readonly res; - constructor(res?: TOk, err?: TErr) { - this.res = res; - this.err = err; - } + constructor(res?: TOk, err?: TErr) { + this.res = res; + this.err = err; + } - public unwrap(): TOk { - if (this.err) { - console.error('Tried to unwrap a Result that contained an error'); - throw this.err; - } + public unwrap(): TOk { + if (this.err) { + console.error('Tried to unwrap a Result that contained an error'); + throw this.err; + } - return this.res!; - } + return this.res!; + } } export class AppError extends Error { - public readonly status: number; - constructor(message: string, status: number) { - super(message); - this.name = 'AppError'; - this.status = status; - Object.setPrototypeOf(this, AppError.prototype); - } + public readonly status: number; + constructor(message: string, status: number) { + super(message); + this.name = 'AppError'; + this.status = status; + Object.setPrototypeOf(this, AppError.prototype); + } - public toFail() { - return fail(this.status, { error: this.message }); - } + public toFail() { + return fail(this.status, { error: this.message }); + } } export type New = Omit; diff --git a/src/lib/server/db/types/user.ts b/src/lib/server/db/types/user.ts index 950b35c..82e6a17 100644 --- a/src/lib/server/db/types/user.ts +++ b/src/lib/server/db/types/user.ts @@ -1,39 +1,39 @@ import { AppError } from '.'; export type User = { - id: string; - name: string; - password: string; - admin: boolean; - groups: string[]; - devices: string[]; + id: string; + name: string; + password: string; + admin: boolean; + groups: string[]; + devices: string[]; }; export type PublicUser = Omit; export function toPublicUser(user: User): PublicUser { - const clonedUser = structuredClone(user) as Partial; - delete clonedUser.password; - return clonedUser as PublicUser; + const clonedUser = structuredClone(user) as Partial; + delete clonedUser.password; + return clonedUser as PublicUser; } export class UserError extends AppError { - constructor(message: string, status: number) { - super(message, status); - this.name = 'UserError'; - Object.setPrototypeOf(this, UserError.prototype); - } + constructor(message: string, status: number) { + super(message, status); + this.name = 'UserError'; + Object.setPrototypeOf(this, UserError.prototype); + } } export namespace UserErrors { - export const NOT_FOUND = new UserError('User not found.', 404); - export const DUPLICATE_NAME = new UserError('User with this username already exists.', 409); - export const UNKNOWN_GROUP = new UserError( - 'One or more of the selected groups do not exist.', - 400, - ); - export const UNKNOWN_DEVICE = new UserError( - 'One or more of the selected devices do not exist.', - 400, - ); + export const NOT_FOUND = new UserError('User not found.', 404); + export const DUPLICATE_NAME = new UserError('User with this username already exists.', 409); + export const UNKNOWN_GROUP = new UserError( + 'One or more of the selected groups do not exist.', + 400, + ); + export const UNKNOWN_DEVICE = new UserError( + 'One or more of the selected devices do not exist.', + 400, + ); } diff --git a/src/lib/server/guard.ts b/src/lib/server/guard.ts index b5ad685..234e062 100644 --- a/src/lib/server/guard.ts +++ b/src/lib/server/guard.ts @@ -2,50 +2,50 @@ import { redirect } from '@sveltejs/kit'; import type { User } from './db/types/user'; export class Guard { - private readonly user?: User; + private readonly user?: User; - private authRequired = false; - private adminRequired = false; + private authRequired = false; + private adminRequired = false; - constructor(user?: User, options?: { authRequired?: boolean; adminRequired?: boolean }) { - this.user = user; - this.authRequired = options?.authRequired ?? false; - this.adminRequired = options?.adminRequired ?? false; - } + constructor(user?: User, options?: { authRequired?: boolean; adminRequired?: boolean }) { + this.user = user; + this.authRequired = options?.authRequired ?? false; + this.adminRequired = options?.adminRequired ?? false; + } - public requiresAuth() { - return new Guard(this.user, { authRequired: true }); - } + public requiresAuth() { + return new Guard(this.user, { authRequired: true }); + } - public requiresAdmin() { - return new Guard(this.user, { authRequired: true, adminRequired: true }); - } + public requiresAdmin() { + return new Guard(this.user, { authRequired: true, adminRequired: true }); + } - public orRedirects() { - if (this.authRequired && !this.user) { - redirect(302, '/login'); - } + public orRedirects() { + if (this.authRequired && !this.user) { + redirect(302, '/login'); + } - if (this.adminRequired && !this.user?.admin) { - redirect(302, '/dash'); - } + if (this.adminRequired && !this.user?.admin) { + redirect(302, '/dash'); + } - return this; - } + return this; + } - public isFailed() { - if (this.authRequired && !this.user) { - return true; - } + public isFailed() { + if (this.authRequired && !this.user) { + return true; + } - if (this.adminRequired && !this.user?.admin) { - return true; - } + if (this.adminRequired && !this.user?.admin) { + return true; + } - return false; - } + return false; + } - public getUser() { - return this.user!; - } + public getUser() { + return this.user!; + } } diff --git a/src/lib/server/sessions.ts b/src/lib/server/sessions.ts index a2c908e..99c28c1 100644 --- a/src/lib/server/sessions.ts +++ b/src/lib/server/sessions.ts @@ -2,29 +2,29 @@ import { nanoid } from 'nanoid'; import { users } from './db'; type SessionData = { - userId: string; - userAgent: string; + userId: string; + userAgent: string; }; const sessions: Map = new Map(); export function createSession(data: SessionData) { - const token = nanoid(); - sessions.set(token, data); - setTimeout(() => sessions.delete(token), 1000 * 60 * 60 * 24); - return token; + const token = nanoid(); + sessions.set(token, data); + setTimeout(() => sessions.delete(token), 1000 * 60 * 60 * 24); + return token; } export async function getUserFromSession(sessionId?: string) { - if (!sessionId) return undefined; + if (!sessionId) return undefined; - const data = sessions.get(sessionId); - if (!data) return undefined; + const data = sessions.get(sessionId); + if (!data) return undefined; - return await users.getById(data.userId); + return await users.getById(data.userId); } export function deleteSession(sessionId?: string) { - if (!sessionId) return; - sessions.delete(sessionId); + if (!sessionId) return; + sessions.delete(sessionId); } diff --git a/src/lib/v2/forms/InputCheckbox.svelte b/src/lib/v2/forms/InputCheckbox.svelte index 9df76fa..e642a61 100644 --- a/src/lib/v2/forms/InputCheckbox.svelte +++ b/src/lib/v2/forms/InputCheckbox.svelte @@ -1,34 +1,36 @@
-
- {label} - - {#snippet children({ checked })} -
+ {label} + + {#snippet children({ checked })} +
- -
- {/snippet} -
-
- {#if description} -

{description}

- {/if} + > + +
+ {/snippet} + +
+ {#if description} +

{description}

+ {/if} diff --git a/src/lib/v2/forms/InputCombobox.svelte b/src/lib/v2/forms/InputCombobox.svelte index 6ee65be..2315fb9 100644 --- a/src/lib/v2/forms/InputCombobox.svelte +++ b/src/lib/v2/forms/InputCombobox.svelte @@ -1,139 +1,139 @@
- inputRef?.focus()} - class="block uppercase mb-2 cursor-pointer" - > - {label} - + inputRef?.focus()} + class="block uppercase mb-2 cursor-pointer" + > + {label} + - { - if (!val) searchTerm = ''; - }} - > -
-
- (searchTerm = e.currentTarget.value.toLowerCase())} - class="bg-neutral-950 px-4 py-2 rounded-l-full + { + if (!val) searchTerm = ''; + }} + > +
+
+ (searchTerm = e.currentTarget.value.toLowerCase())} + class="bg-neutral-950 px-4 py-2 rounded-l-full focus:ring-indigo-500 focus:border-indigo-500 {extraClasses} z-1" - /> - -
- -
- Select - -
-
-
- - + Select + +
+ +
+ + - {#snippet child({ wrapperProps, props, open })} - {#if open} -
-
- -
+
- - - - {#if searchFiltered.length > 0} - {#each searchFiltered as el} - - {#snippet children({ selected })} - {el.name} - - {/snippet} - - {/each} - {:else} -

- No results found. Try another term. -

- {/if} -
- - -
-
-
-
- {/if} - {/snippet} - - - + > + {#snippet children({ selected })} + {el.name} + + {/snippet} + + {/each} + {:else} +

+ No results found. Try another term. +

+ {/if} + + + +
+
+
+
+ {/if} + {/snippet} + + + - {#if description} -

{description}

- {/if} + {#if description} +

{description}

+ {/if} diff --git a/src/lib/v2/forms/InputText.svelte b/src/lib/v2/forms/InputText.svelte index 751f51d..5a445e9 100644 --- a/src/lib/v2/forms/InputText.svelte +++ b/src/lib/v2/forms/InputText.svelte @@ -1,29 +1,29 @@
- {label} - {label} + - {#if description} -

{description}

- {/if} + /> + {#if description} +

{description}

+ {/if}
diff --git a/src/lib/v2/snippets/EditPage.svelte b/src/lib/v2/snippets/EditPage.svelte index af327fe..98a8d42 100644 --- a/src/lib/v2/snippets/EditPage.svelte +++ b/src/lib/v2/snippets/EditPage.svelte @@ -1,52 +1,52 @@
-
- {@render children()} + + {@render children()} -
-
- +
+ - -

Save

-
+ > + +

Save

+ - history.back()} - class="flex items-center gap-2 text-white cursor-pointer + history.back()} + class="flex items-center gap-2 text-white cursor-pointer bg-neutral-800 hover:bg-neutral-700 hover:scale-95 transition-all duration-300 ease-in-out rounded-full py-2 px-4 border border-neutral-600" - > - -

Cancel

-
-
+ > + +

Cancel

+
+
- {#if !createOnly} - - -

Delete

-
- {/if} -
-
+ > + +

Delete

+ + {/if} +
+ diff --git a/src/lib/v2/snippets/ListPage.svelte b/src/lib/v2/snippets/ListPage.svelte index 657572e..9f0b6da 100644 --- a/src/lib/v2/snippets/ListPage.svelte +++ b/src/lib/v2/snippets/ListPage.svelte @@ -1,32 +1,32 @@ {#if createHref} - - - - + > + + + {/if}
- {#if children} - {@render children?.()} - {:else} -
-

Nothing here.

- {#if createHref} -

Get started by clicking "{msgAdd}".

- {/if} -
- {/if} + {#if children} + {@render children?.()} + {:else} +
+

Nothing here.

+ {#if createHref} +

Get started by clicking "{msgAdd}".

+ {/if} +
+ {/if}
diff --git a/src/lib/v2/transitions/slideFade.ts b/src/lib/v2/transitions/slideFade.ts index a9e971c..2c6b32a 100644 --- a/src/lib/v2/transitions/slideFade.ts +++ b/src/lib/v2/transitions/slideFade.ts @@ -8,38 +8,38 @@ import type { TransitionConfig } from 'svelte/transition'; // why? made no sense, opacity changed so fast you couldn't see it... oh well export function slideFade( - node: Element, - { delay = 0, duration = 300, easing = cubicOut, axis = 'y' } = {}, + node: Element, + { delay = 0, duration = 300, easing = cubicOut, axis = 'y' } = {}, ): TransitionConfig { - const style = getComputedStyle(node); + const style = getComputedStyle(node); - const opacity = +style.opacity; + const opacity = +style.opacity; - const primaryProp = axis === 'y' ? 'height' : 'width'; - const primaryVal = parseFloat(style.getPropertyValue(primaryProp)); + const primaryProp = axis === 'y' ? 'height' : 'width'; + const primaryVal = parseFloat(style.getPropertyValue(primaryProp)); - const secondaryProps = axis === 'y' ? ['top', 'bottom'] : ['left', 'right']; - const padStart = parseFloat(style.getPropertyValue(`padding-${secondaryProps[0]}`)); - const padEnd = parseFloat(style.getPropertyValue(`padding-${secondaryProps[1]}`)); - const marginStart = parseFloat(style.getPropertyValue(`margin-${secondaryProps[0]}`)); - const marginEnd = parseFloat(style.getPropertyValue(`margin-${secondaryProps[1]}`)); - const borderWidthStart = parseFloat(style.getPropertyValue(`border-${secondaryProps[0]}-width`)); - const borderWidthEnd = parseFloat(style.getPropertyValue(`border-${secondaryProps[1]}-width`)); + const secondaryProps = axis === 'y' ? ['top', 'bottom'] : ['left', 'right']; + const padStart = parseFloat(style.getPropertyValue(`padding-${secondaryProps[0]}`)); + const padEnd = parseFloat(style.getPropertyValue(`padding-${secondaryProps[1]}`)); + const marginStart = parseFloat(style.getPropertyValue(`margin-${secondaryProps[0]}`)); + const marginEnd = parseFloat(style.getPropertyValue(`margin-${secondaryProps[1]}`)); + const borderWidthStart = parseFloat(style.getPropertyValue(`border-${secondaryProps[0]}-width`)); + const borderWidthEnd = parseFloat(style.getPropertyValue(`border-${secondaryProps[1]}-width`)); - return { - delay, - duration, - easing, - css: (t) => - 'overflow: hidden;' + - `opacity: ${t * opacity};` + - `${primaryProp}: ${t * primaryVal}px;` + - `padding-${secondaryProps[0]}: ${t * padStart}px;` + - `padding-${secondaryProps[1]}: ${t * padEnd}px;` + - `margin-${secondaryProps[0]}: ${t * marginStart}px;` + - `margin-${secondaryProps[1]}: ${t * marginEnd}px;` + - `border-${secondaryProps[0]}-width: ${t * borderWidthStart}px;` + - `border-${secondaryProps[1]}-width: ${t * borderWidthEnd}px;` + - `min-${primaryProp}: 0`, - }; + return { + delay, + duration, + easing, + css: (t) => + 'overflow: hidden;' + + `opacity: ${t * opacity};` + + `${primaryProp}: ${t * primaryVal}px;` + + `padding-${secondaryProps[0]}: ${t * padStart}px;` + + `padding-${secondaryProps[1]}: ${t * padEnd}px;` + + `margin-${secondaryProps[0]}: ${t * marginStart}px;` + + `margin-${secondaryProps[1]}: ${t * marginEnd}px;` + + `border-${secondaryProps[0]}-width: ${t * borderWidthStart}px;` + + `border-${secondaryProps[1]}-width: ${t * borderWidthEnd}px;` + + `min-${primaryProp}: 0`, + }; } diff --git a/src/lib/v2/ui/Collapsible.svelte b/src/lib/v2/ui/Collapsible.svelte index e912f5a..3e2cf29 100644 --- a/src/lib/v2/ui/Collapsible.svelte +++ b/src/lib/v2/ui/Collapsible.svelte @@ -1,38 +1,38 @@
- (expanded = !expanded)} - class="flex items-center gap-2 cursor-pointer text-neutral-500 uppercase text-sm" - > - - {label} - + (expanded = !expanded)} + class="flex items-center gap-2 cursor-pointer text-neutral-500 uppercase text-sm" + > + + {label} + - - - - {#if expanded} -
- {@render children()} -
- {:else if renderHidden} - - - {/if} + + + + {#if expanded} +
+ {@render children()} +
+ {:else if renderHidden} + + + {/if}
diff --git a/src/lib/v2/ui/NavBar.svelte b/src/lib/v2/ui/NavBar.svelte index bd48047..bf7d1c0 100644 --- a/src/lib/v2/ui/NavBar.svelte +++ b/src/lib/v2/ui/NavBar.svelte @@ -1,13 +1,13 @@ diff --git a/src/lib/v2/ui/NavBarLink.svelte b/src/lib/v2/ui/NavBarLink.svelte index df5f09f..8a3eea6 100644 --- a/src/lib/v2/ui/NavBarLink.svelte +++ b/src/lib/v2/ui/NavBarLink.svelte @@ -1,24 +1,24 @@
  • - - {#if Icon} - - {/if} + /> + {/if} - {@render children?.()} - + {@render children?.()} +
  • diff --git a/src/lib/v2/ui/ResCard.svelte b/src/lib/v2/ui/ResCard.svelte index 857ccd2..601c923 100644 --- a/src/lib/v2/ui/ResCard.svelte +++ b/src/lib/v2/ui/ResCard.svelte @@ -1,37 +1,37 @@
    -
    -

    {title}

    -

    {subtitle}

    -
    +
    +

    {title}

    +

    {subtitle}

    +
    -
    - {#if editHref} - + {#if editHref} + - - - {/if} + href={editHref} + > + + + {/if} - {#if wakePost} - - - - {/if} -
    + > + + + {/if} +
    diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index b93e9ba..5d83456 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,7 +1,7 @@ {@render children()} diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index e16994d..572dca4 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -2,5 +2,5 @@ import { redirect } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async () => { - redirect(302, '/dash'); + redirect(302, '/dash'); }; diff --git a/src/routes/dash/+layout.server.ts b/src/routes/dash/+layout.server.ts index efc2f17..c357922 100644 --- a/src/routes/dash/+layout.server.ts +++ b/src/routes/dash/+layout.server.ts @@ -2,9 +2,9 @@ import { toPublicUser } from '$lib/server/db/types/user'; import type { LayoutServerLoad } from './$types'; export const load: LayoutServerLoad = async ({ locals: { guard } }) => { - const user = guard.requiresAuth().orRedirects().getUser(); + const user = guard.requiresAuth().orRedirects().getUser(); - return { - user: toPublicUser(user), - }; + return { + user: toPublicUser(user), + }; }; diff --git a/src/routes/dash/+layout.svelte b/src/routes/dash/+layout.svelte index 24e31f5..7ab4a52 100644 --- a/src/routes/dash/+layout.svelte +++ b/src/routes/dash/+layout.svelte @@ -1,98 +1,98 @@ - Dashboard - {$pageTitle} + Dashboard - {$pageTitle}
    - - Devices - Users - Groups - -
    -
    - -

    {$pageTitle}

    -
    - - - Devices + Users + Groups + +
    +
    + +

    {$pageTitle}

    +
    + + + - {data.user.name} - - - - -
    + > +

    Logout

    + + + +
    +
    + {/if} + {/snippet} + + +
    - {@render children()} + {@render children()}
    diff --git a/src/routes/dash/+page.server.ts b/src/routes/dash/+page.server.ts index 6be87dd..5a92c47 100644 --- a/src/routes/dash/+page.server.ts +++ b/src/routes/dash/+page.server.ts @@ -1,6 +1,6 @@ import { redirect, type ServerLoad } from '@sveltejs/kit'; export const load: ServerLoad = async ({ locals: { guard } }) => { - guard.requiresAuth().orRedirects(); - redirect(302, '/dash/devices'); + guard.requiresAuth().orRedirects(); + redirect(302, '/dash/devices'); }; diff --git a/src/routes/dash/devices/+page.server.ts b/src/routes/dash/devices/+page.server.ts index bd633ba..4cf44c6 100644 --- a/src/routes/dash/devices/+page.server.ts +++ b/src/routes/dash/devices/+page.server.ts @@ -2,8 +2,8 @@ import { users } from '$lib/server/db'; import type { ServerLoad } from '@sveltejs/kit'; export const load: ServerLoad = async ({ locals: { guard } }) => { - const user = guard.requiresAuth().orRedirects().getUser(); - return { - devices: await users.fetchDevices(user.id), - }; + const user = guard.requiresAuth().orRedirects().getUser(); + return { + devices: await users.fetchDevices(user.id), + }; }; diff --git a/src/routes/dash/devices/+page.svelte b/src/routes/dash/devices/+page.svelte index f79d5ce..1c73430 100644 --- a/src/routes/dash/devices/+page.svelte +++ b/src/routes/dash/devices/+page.svelte @@ -1,22 +1,22 @@ -
    - {#each data.devices as device} - - {/each} -
    +
    + {#each data.devices as device} + + {/each} +
    diff --git a/src/routes/dash/devices/[id]/+page.server.ts b/src/routes/dash/devices/[id]/+page.server.ts index 77eeafc..12821d7 100644 --- a/src/routes/dash/devices/[id]/+page.server.ts +++ b/src/routes/dash/devices/[id]/+page.server.ts @@ -6,91 +6,91 @@ import { wake } from 'wake_on_lan'; import { z } from 'zod'; export const load: ServerLoad = async ({ locals: { guard }, params }) => { - guard.requiresAdmin().orRedirects(); + guard.requiresAdmin().orRedirects(); - const device = await devices.getById(params.id ?? ''); + const device = await devices.getById(params.id ?? ''); - if (!device && params.id !== 'new') { - redirect(302, '/dash/devices'); - } + if (!device && params.id !== 'new') { + redirect(302, '/dash/devices'); + } - return { - device, - }; + return { + device, + }; }; export const actions = { - update: async ({ request, params, locals: { guard } }) => { - if (guard.requiresAdmin().isFailed()) return FORBIDDEN; + update: async ({ request, params, locals: { guard } }) => { + if (guard.requiresAdmin().isFailed()) return FORBIDDEN; - const form = await request.formData(); + 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.' }), - mac: z - .string({ message: 'MAC address is required.' }) - .refine((v) => validator.isMACAddress(v), { message: 'Invalid MAC address.' }), - broadcast: z - .string() - .refine((v) => validator.isIP(v), { message: 'Invalid broadcast IP address.' }), - port: z.coerce - .number({ message: 'Port is invalid.' }) - .min(1, { message: 'Port must be at least 1.' }) - .max(65535, { message: 'Port must be at most 65535.' }), - packets: z.coerce - .number({ message: 'Packets quantity is invalid.' }) - .min(1, { message: 'Packets quantity must be at least 1.' }) - .max(50, { message: 'Packets quantity must be at most 50.' }), - }); + 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.' }), + mac: z + .string({ message: 'MAC address is required.' }) + .refine((v) => validator.isMACAddress(v), { message: 'Invalid MAC address.' }), + broadcast: z + .string() + .refine((v) => validator.isIP(v), { message: 'Invalid broadcast IP address.' }), + port: z.coerce + .number({ message: 'Port is invalid.' }) + .min(1, { message: 'Port must be at least 1.' }) + .max(65535, { message: 'Port must be at most 65535.' }), + packets: z.coerce + .number({ message: 'Packets quantity is invalid.' }) + .min(1, { message: 'Packets quantity must be at least 1.' }) + .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({ + name: form.get('name'), + mac: form.get('mac'), + broadcast: form.get('broadcast'), + port: form.get('port'), + packets: form.get('packets'), + }); - if (!parsed.success) { - return PARSE_ERROR(parsed.error); - } + if (!parsed.success) { + return PARSE_ERROR(parsed.error); + } - const err = - params.id === 'new' - ? await devices.create(parsed.data) - : await devices.update({ id: params.id ?? '', ...parsed.data }); + const err = + params.id === 'new' + ? await devices.create(parsed.data) + : await devices.update({ id: params.id ?? '', ...parsed.data }); - return err ? err.toFail() : SUCCESS; - }, + return err ? err.toFail() : SUCCESS; + }, - delete: async ({ locals: { guard }, params }) => { - if (guard.requiresAdmin().isFailed()) return FORBIDDEN; + delete: async ({ locals: { guard }, params }) => { + if (guard.requiresAdmin().isFailed()) return FORBIDDEN; - const err = await devices.delete(params.id ?? ''); - return err ? err.toFail() : SUCCESS; - }, + const err = await devices.delete(params.id ?? ''); + return err ? err.toFail() : SUCCESS; + }, - wake: async ({ params, locals: { guard } }) => { - guard = guard.requiresAuth(); - if (guard.isFailed()) return FORBIDDEN; + 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); + const device = (await users.fetchDevices(guard.getUser().id)).find((d) => d.id === params.id); - if (!device) { - return fail(404); - } + if (!device) { + return fail(404); + } - console.log('Trying to wake ' + device.name); + console.log('Trying to wake ' + device.name); - wake(device.mac, { - address: device.broadcast, - port: device.port, - num_packets: device.packets, - }); + wake(device.mac, { + address: device.broadcast, + port: device.port, + num_packets: device.packets, + }); - return SUCCESS; - }, + return SUCCESS; + }, } satisfies Actions; diff --git a/src/routes/dash/devices/[id]/+page.svelte b/src/routes/dash/devices/[id]/+page.svelte index 3f4433f..c4eac6e 100644 --- a/src/routes/dash/devices/[id]/+page.svelte +++ b/src/routes/dash/devices/[id]/+page.svelte @@ -1,73 +1,73 @@ - + - + - - + + - + - - + + diff --git a/src/routes/dash/groups/+page.server.ts b/src/routes/dash/groups/+page.server.ts index fbce92c..6e62d9a 100644 --- a/src/routes/dash/groups/+page.server.ts +++ b/src/routes/dash/groups/+page.server.ts @@ -2,17 +2,17 @@ import { groups } from '$lib/server/db'; import { type ServerLoad } from '@sveltejs/kit'; export const load: ServerLoad = async ({ locals: { guard } }) => { - guard.requiresAdmin().orRedirects(); + guard.requiresAdmin().orRedirects(); - const allGroups = await groups.getAll(); - let userCounts: { [key: string]: number } = {}; + const allGroups = await groups.getAll(); + let userCounts: { [key: string]: number } = {}; - for (let g of allGroups) { - userCounts[g.id] = await groups.countUsers(g.id); - } + for (let g of allGroups) { + userCounts[g.id] = await groups.countUsers(g.id); + } - return { - groups: allGroups, - userCounts, - }; + return { + groups: allGroups, + userCounts, + }; }; diff --git a/src/routes/dash/groups/+page.svelte b/src/routes/dash/groups/+page.svelte index 4a44b4c..3dceba3 100644 --- a/src/routes/dash/groups/+page.svelte +++ b/src/routes/dash/groups/+page.svelte @@ -1,21 +1,21 @@ -
    - {#each data.groups as group} - - {/each} -
    +
    + {#each data.groups as group} + + {/each} +
    diff --git a/src/routes/dash/groups/[id]/+page.server.ts b/src/routes/dash/groups/[id]/+page.server.ts index 66a4576..a429b9f 100644 --- a/src/routes/dash/groups/[id]/+page.server.ts +++ b/src/routes/dash/groups/[id]/+page.server.ts @@ -4,55 +4,55 @@ import { redirect, type Actions, type ServerLoad } from '@sveltejs/kit'; import { z } from 'zod'; export const load: ServerLoad = async ({ locals: { guard }, params }) => { - guard.requiresAdmin().orRedirects(); + guard.requiresAdmin().orRedirects(); - const group = await groups.getById(params.id ?? ''); + const group = await groups.getById(params.id ?? ''); - if (!group && params.id !== 'new') { - redirect(302, '/dash/groups'); - } + if (!group && params.id !== 'new') { + redirect(302, '/dash/groups'); + } - return { - devices: await devices.getAll(), - group, - }; + return { + devices: await devices.getAll(), + group, + }; }; export const actions = { - update: async ({ request, locals: { guard }, params }) => { - if (guard.requiresAdmin().isFailed()) return FORBIDDEN; + update: async ({ request, locals: { guard }, params }) => { + if (guard.requiresAdmin().isFailed()) return FORBIDDEN; - const form = await request.formData(); + 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.' }), - devices: z.array(z.string()), - }); + 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.' }), + devices: z.array(z.string()), + }); - const parsed = schema.safeParse({ - name: form.get('name'), - devices: form.getAll('devices'), - }); + const parsed = schema.safeParse({ + name: form.get('name'), + devices: form.getAll('devices'), + }); - if (!parsed.success) { - return PARSE_ERROR(parsed.error); - } + if (!parsed.success) { + return PARSE_ERROR(parsed.error); + } - const err = - params.id === 'new' - ? await groups.create({ ...parsed.data }) - : await groups.update({ id: params.id ?? '', ...parsed.data }); + const err = + params.id === 'new' + ? await groups.create({ ...parsed.data }) + : await groups.update({ id: params.id ?? '', ...parsed.data }); - return err ? err.toFail() : SUCCESS; - }, + return err ? err.toFail() : SUCCESS; + }, - delete: async ({ locals: { guard }, params }) => { - if (guard.requiresAdmin().isFailed()) return FORBIDDEN; + delete: async ({ locals: { guard }, params }) => { + if (guard.requiresAdmin().isFailed()) return FORBIDDEN; - const err = await groups.delete(params.id ?? ''); - return err ? err.toFail() : SUCCESS; - }, + const err = await groups.delete(params.id ?? ''); + return err ? err.toFail() : SUCCESS; + }, } satisfies Actions; diff --git a/src/routes/dash/groups/[id]/+page.svelte b/src/routes/dash/groups/[id]/+page.svelte index 993304b..f46ebd6 100644 --- a/src/routes/dash/groups/[id]/+page.svelte +++ b/src/routes/dash/groups/[id]/+page.svelte @@ -1,41 +1,41 @@ - + - ({ - value: d.id, - name: d.name, - selected: data.group?.devices.find((devId) => devId === d.id) ? true : false, - }))} - /> + ({ + value: d.id, + name: d.name, + selected: data.group?.devices.find((devId) => devId === d.id) ? true : false, + }))} + /> diff --git a/src/routes/dash/users/+page.server.ts b/src/routes/dash/users/+page.server.ts index 4df5a48..475c1f9 100644 --- a/src/routes/dash/users/+page.server.ts +++ b/src/routes/dash/users/+page.server.ts @@ -1,11 +1,11 @@ import { users } from '$lib/server/db'; -import { toPublicUser, type User } from '$lib/server/db/types/user'; +import { toPublicUser } from '$lib/server/db/types/user'; import type { ServerLoad } from '@sveltejs/kit'; export const load: ServerLoad = async ({ locals: { guard } }) => { - guard.requiresAdmin().orRedirects(); + guard.requiresAdmin().orRedirects(); - return { - users: (await users.getAll()).map((u) => toPublicUser(u)), - }; + return { + users: (await users.getAll()).map((u) => toPublicUser(u)), + }; }; diff --git a/src/routes/dash/users/+page.svelte b/src/routes/dash/users/+page.svelte index 99e9ece..703cdd0 100644 --- a/src/routes/dash/users/+page.svelte +++ b/src/routes/dash/users/+page.svelte @@ -1,21 +1,21 @@ -
    - {#each data.users as user} - - {/each} -
    +
    + {#each data.users as user} + + {/each} +
    diff --git a/src/routes/dash/users/[id]/+page.server.ts b/src/routes/dash/users/[id]/+page.server.ts index da5feb2..b9237dc 100644 --- a/src/routes/dash/users/[id]/+page.server.ts +++ b/src/routes/dash/users/[id]/+page.server.ts @@ -7,85 +7,85 @@ import bcrypt from 'bcryptjs'; import { z } from 'zod'; export const load: ServerLoad = async ({ locals: { guard }, params }) => { - guard.requiresAdmin().orRedirects(); + guard.requiresAdmin().orRedirects(); - let user = await users.getById(params.id ?? ''); + let user = await users.getById(params.id ?? ''); - if (!user && params.id !== 'new') { - redirect(302, '/dash/users'); - } + if (!user && params.id !== 'new') { + redirect(302, '/dash/users'); + } - return { - user: toPublicUser(user!), - groups: await groups.getAll(), - devices: await devices.getAll(), - }; + return { + user: toPublicUser(user!), + groups: await groups.getAll(), + devices: await devices.getAll(), + }; }; export const actions = { - update: async ({ request, locals: { guard }, params }) => { - if (guard.requiresAdmin().isFailed()) return FORBIDDEN; + update: async ({ request, locals: { guard }, params }) => { + if (guard.requiresAdmin().isFailed()) return FORBIDDEN; - const form = await request.formData(); + 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() - .refine((v) => (params.id === 'new' ? v.length > 0 : true), { - // if /new, password must be present - message: 'Password is required at user creation.', - }) - .refine((v) => (v.length > 0 ? v.length >= 8 : true), { - // if password present, must be at least 8 chars - message: 'Password must be at least 8 characters.', - }), - groups: z.array(z.string()), - devices: z.array(z.string()), - }); + 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() + .refine((v) => (params.id === 'new' ? v.length > 0 : true), { + // if /new, password must be present + message: 'Password is required at user creation.', + }) + .refine((v) => (v.length > 0 ? v.length >= 8 : true), { + // if password present, must be at least 8 chars + message: 'Password must be at least 8 characters.', + }), + groups: z.array(z.string()), + 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({ + 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 PARSE_ERROR(parsed.error); - } + if (!parsed.success) { + return PARSE_ERROR(parsed.error); + } - if (params.id === 'new') { - const err = await users.create({ - ...parsed.data, - password: bcrypt.hashSync(parsed.data.password, 10), - }); + if (params.id === 'new') { + const err = await users.create({ + ...parsed.data, + password: bcrypt.hashSync(parsed.data.password, 10), + }); - return err ? err.toFail() : SUCCESS; - } + return err ? err.toFail() : SUCCESS; + } - let updatedUser: Updated = { id: params.id ?? '', ...parsed.data }; + let updatedUser: Updated = { id: params.id ?? '', ...parsed.data }; - if (parsed.data.password.length > 0) { - updatedUser.password = bcrypt.hashSync(parsed.data.password, 10); - } else { - // avoid saving empty passwpord - delete updatedUser.password; - } + if (parsed.data.password.length > 0) { + updatedUser.password = bcrypt.hashSync(parsed.data.password, 10); + } else { + // avoid saving empty passwpord + delete updatedUser.password; + } - const err = await users.update(updatedUser); - return err ? err.toFail() : SUCCESS; - }, + const err = await users.update(updatedUser); + return err ? err.toFail() : SUCCESS; + }, - delete: async ({ locals: { guard }, params }) => { - if (guard.requiresAdmin().isFailed()) return FORBIDDEN; + delete: async ({ locals: { guard }, params }) => { + if (guard.requiresAdmin().isFailed()) return FORBIDDEN; - const err = await users.delete(params.id ?? ''); - return err ? err.toFail() : SUCCESS; - }, + const err = await users.delete(params.id ?? ''); + return err ? err.toFail() : SUCCESS; + }, } satisfies Actions; diff --git a/src/routes/dash/users/[id]/+page.svelte b/src/routes/dash/users/[id]/+page.svelte index df722d9..4fa0350 100644 --- a/src/routes/dash/users/[id]/+page.svelte +++ b/src/routes/dash/users/[id]/+page.svelte @@ -1,72 +1,72 @@

    {form?.error}

    - + - + - + - ({ - value: d.id, - name: d.name, - selected: data.user?.devices.find((devId) => devId === d.id) ? true : false, - }))} - /> + ({ + value: d.id, + name: d.name, + selected: data.user?.devices.find((devId) => devId === d.id) ? true : false, + }))} + /> - ({ - value: g.id, - name: g.name, - selected: data.user?.groups.find((groupId) => groupId === g.id) ? true : false, - }))} - /> + ({ + value: g.id, + name: g.name, + selected: data.user?.groups.find((groupId) => groupId === g.id) ? true : false, + }))} + /> diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts index 4b59b7b..fa7e449 100644 --- a/src/routes/login/+page.server.ts +++ b/src/routes/login/+page.server.ts @@ -1,49 +1,49 @@ +import { dev } from '$app/environment'; import { users } from '$lib/server/db'; import { createSession } from '$lib/server/sessions'; import { fail, redirect } from '@sveltejs/kit'; import bcrypt from 'bcryptjs'; import type { Actions } from './$types'; -import { dev } from '$app/environment'; export const actions = { - default: async ({ cookies, request, locals: { guard } }) => { - if (!guard.requiresAuth().isFailed()) { - redirect(302, '/dash'); - } + default: async ({ cookies, request, locals: { guard } }) => { + if (!guard.requiresAuth().isFailed()) { + redirect(302, '/dash'); + } - const data = await request.formData(); - const username = data.get('username')?.toString(); - const password = data.get('password')?.toString(); + const data = await request.formData(); + const username = data.get('username')?.toString(); + const password = data.get('password')?.toString(); - if (!username || !password) { - return fail(400, { - error: 'MISSING_CREDENTIALS', - }); - } + if (!username || !password) { + return fail(400, { + error: 'MISSING_CREDENTIALS', + }); + } - const user = await users.getByName(username); + const user = await users.getByName(username); - if (!user || !bcrypt.compareSync(password, user.password)) { - return fail(403, { - error: 'INVALID_CREDENTIALS', - }); - } + if (!user || !bcrypt.compareSync(password, user.password)) { + return fail(403, { + error: 'INVALID_CREDENTIALS', + }); + } - cookies.set( - 'session', - createSession({ - userAgent: request.headers.get('user-agent') ?? 'UNKNOWN', - userId: user.id, - }), - { - path: '/', - httpOnly: true, - secure: !dev, // safari doesn't allow secure cookies on localhost - sameSite: true, - maxAge: 60 * 60 * 24, - }, - ); + cookies.set( + 'session', + createSession({ + userAgent: request.headers.get('user-agent') ?? 'UNKNOWN', + userId: user.id, + }), + { + path: '/', + httpOnly: true, + secure: !dev, // safari doesn't allow secure cookies on localhost + sameSite: true, + maxAge: 60 * 60 * 24, + }, + ); - redirect(302, '/dash'); - }, + redirect(302, '/dash'); + }, } satisfies Actions; diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index 4f47f1c..d43d286 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -1,21 +1,21 @@
    - - - + + +
    {#if form?.error} -

    Could not login: {form.error}

    -{/if} \ No newline at end of file +

    Could not login: {form.error}

    +{/if} diff --git a/src/routes/logout/+page.server.ts b/src/routes/logout/+page.server.ts index 846136f..9cb52e1 100644 --- a/src/routes/logout/+page.server.ts +++ b/src/routes/logout/+page.server.ts @@ -1,8 +1,8 @@ -import { deleteSession } from "$lib/server/sessions"; -import { redirect, type ServerLoad } from "@sveltejs/kit"; +import { deleteSession } from '$lib/server/sessions'; +import { redirect, type ServerLoad } from '@sveltejs/kit'; export const load: ServerLoad = async ({ cookies }) => { - deleteSession(cookies.get("session")); - cookies.delete("session", { path: "/" }); - redirect(302, "/login"); -}; \ No newline at end of file + deleteSession(cookies.get('session')); + cookies.delete('session', { path: '/' }); + redirect(302, '/login'); +};