went back to lowdb
This commit is contained in:
parent
c419d57754
commit
54e988bd0a
5
.gitignore
vendored
5
.gitignore
vendored
@ -24,7 +24,4 @@ vite.config.ts.timestamp-*
|
||||
|
||||
# Have empty data folder ready to go
|
||||
/data/*
|
||||
!/data/.gitkeep
|
||||
|
||||
# Generated prisma client
|
||||
generated/prisma/
|
||||
!/data/.gitkeep
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [
|
||||
|
||||
68
db.json
Normal file
68
db.json
Normal file
@ -0,0 +1,68 @@
|
||||
{
|
||||
"users": [
|
||||
{
|
||||
"id": "EqWthpxM6dwLpdg6_GMNv",
|
||||
"name": "admin",
|
||||
"password": "$2b$10$M5f38oxNH2TbtJrSeR5bi.qgz9n5rhnxrQPh3Y.8GIZaH1Z6MgZDa",
|
||||
"admin": true,
|
||||
"groups": [],
|
||||
"devices": [
|
||||
"2PAUDI9gbzwuD8ptcptDM",
|
||||
"A6u-0SaX5q5s_DzMFf3N1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "l6GVetq1ifjsHmnoy3TrC",
|
||||
"name": "axel",
|
||||
"admin": false,
|
||||
"groups": [
|
||||
"2Y1OFiRWu8r50_WBLaB-j"
|
||||
],
|
||||
"devices": [
|
||||
"ps8lzN3c2uHcGtqHdmaEz"
|
||||
],
|
||||
"password": "$2b$10$MGb8xG3a4oxIvFq6fiqG8.X8HKS1j8WdjYjc6fgO6W.5s0vvQi8A."
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": "2Y1OFiRWu8r50_WBLaB-j",
|
||||
"name": "Gr1",
|
||||
"devices": [
|
||||
"2PAUDI9gbzwuD8ptcptDM",
|
||||
"ps8lzN3c2uHcGtqHdmaEz"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "gNaTU4Tdj-6-a8wRyWf21",
|
||||
"name": "Gr2",
|
||||
"devices": []
|
||||
}
|
||||
],
|
||||
"devices": [
|
||||
{
|
||||
"id": "2PAUDI9gbzwuD8ptcptDM",
|
||||
"name": "Dev1",
|
||||
"mac": "00:00:00:00:00:00",
|
||||
"broadcast": "255.255.255.254",
|
||||
"port": 9,
|
||||
"packets": 3
|
||||
},
|
||||
{
|
||||
"id": "ps8lzN3c2uHcGtqHdmaEz",
|
||||
"name": "Dev2",
|
||||
"mac": "00:00:00:00:00:00",
|
||||
"broadcast": "255.255.255.255",
|
||||
"port": 9,
|
||||
"packets": 3
|
||||
},
|
||||
{
|
||||
"id": "A6u-0SaX5q5s_DzMFf3N1",
|
||||
"name": "Dev3",
|
||||
"mac": "00:00:00:00:00:00",
|
||||
"broadcast": "255.255.255.255",
|
||||
"port": 9,
|
||||
"packets": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
33
package-lock.json
generated
33
package-lock.json
generated
@ -8,7 +8,6 @@
|
||||
"name": "my-app",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.5.0",
|
||||
"@types/wake_on_lan": "^0.0.33",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"drizzle-orm": "^0.41.0",
|
||||
@ -24,7 +23,6 @@
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"prisma": "^6.5.0",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
@ -774,6 +772,8 @@
|
||||
"integrity": "sha512-M6w1Ql/BeiGoZmhMdAZUXHu5sz5HubyVcKukbLs3l0ELcQb8hTUJxtGEChhv4SVJ0QJlwtLnwOLgIRQhpsm9dw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18.18"
|
||||
},
|
||||
@ -794,8 +794,9 @@
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.5.0.tgz",
|
||||
"integrity": "sha512-sOH/2Go9Zer67DNFLZk6pYOHj+rumSb0VILgltkoxOjYnlLqUpHPAN826vnx8HigqnOCxj9LRhT6U7uLiIIWgw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": ">=0.12 <1",
|
||||
"esbuild-register": "3.6.0"
|
||||
@ -805,16 +806,18 @@
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.5.0.tgz",
|
||||
"integrity": "sha512-fc/nusYBlJMzDmDepdUtH9aBsJrda2JNErP9AzuHbgUEQY0/9zQYZdNlXmKoIWENtio+qarPNe/+DQtrX5kMcQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0"
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.5.0.tgz",
|
||||
"integrity": "sha512-FVPQYHgOllJklN9DUyujXvh3hFJCY0NX86sDmBErLvoZjy2OXGiZ5FNf3J/C4/RZZmCypZBYpBKEhx7b7rEsdw==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.5.0",
|
||||
"@prisma/engines-version": "6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60",
|
||||
@ -826,15 +829,17 @@
|
||||
"version": "6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60.tgz",
|
||||
"integrity": "sha512-iK3EmiVGFDCmXjSpdsKGNqy9hOdLnvYBrJB61far/oP03hlIxrb04OWmDjNTwtmZ3UZdA5MCvI+f+3k2jPTflQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0"
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.5.0.tgz",
|
||||
"integrity": "sha512-3LhYA+FXP6pqY8FLHCjewyE8pGXXJ7BxZw2rhPq+CZAhvflVzq4K8Qly3OrmOkn6wGlz79nyLQdknyCG2HBTuA==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.5.0",
|
||||
"@prisma/engines-version": "6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60",
|
||||
@ -845,8 +850,9 @@
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.5.0.tgz",
|
||||
"integrity": "sha512-xYcvyJwNMg2eDptBYFqFLUCfgi+wZLcj6HDMsj0Qw0irvauG4IKmkbywnqwok0B+k+W+p+jThM2DKTSmoPCkzw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.5.0"
|
||||
}
|
||||
@ -1841,8 +1847,9 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz",
|
||||
"integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
@ -1932,7 +1939,6 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -2638,9 +2644,10 @@
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.5.0.tgz",
|
||||
"integrity": "sha512-yUGXmWqv5F4PByMSNbYFxke/WbnyTLjnJ5bKr8fLkcnY7U5rU9rUTh/+Fja+gOrRxEgtCbCtca94IeITj4j/pg==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@prisma/config": "6.5.0",
|
||||
"@prisma/engines": "6.5.0"
|
||||
|
||||
@ -21,7 +21,6 @@
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"prisma": "^6.5.0",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"tailwindcss": "^4.0.0",
|
||||
@ -31,7 +30,6 @@
|
||||
"vite": "^6.2.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.5.0",
|
||||
"@types/wake_on_lan": "^0.0.33",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"drizzle-orm": "^0.41.0",
|
||||
|
||||
@ -1,77 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"password" TEXT NOT NULL,
|
||||
"admin" BOOLEAN NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Group" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Device" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"mac" TEXT NOT NULL,
|
||||
"broadcast" TEXT NOT NULL DEFAULT '255.255.255.255',
|
||||
"port" INTEGER NOT NULL DEFAULT 9,
|
||||
"packets" INTEGER NOT NULL DEFAULT 3
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_GroupToUser" (
|
||||
"A" INTEGER NOT NULL,
|
||||
"B" INTEGER NOT NULL,
|
||||
CONSTRAINT "_GroupToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "Group" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "_GroupToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_DeviceToUser" (
|
||||
"A" INTEGER NOT NULL,
|
||||
"B" INTEGER NOT NULL,
|
||||
CONSTRAINT "_DeviceToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "Device" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "_DeviceToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_DeviceToGroup" (
|
||||
"A" INTEGER NOT NULL,
|
||||
"B" INTEGER NOT NULL,
|
||||
CONSTRAINT "_DeviceToGroup_A_fkey" FOREIGN KEY ("A") REFERENCES "Device" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "_DeviceToGroup_B_fkey" FOREIGN KEY ("B") REFERENCES "Group" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_name_key" ON "User"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Group_name_key" ON "Group"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Device_name_key" ON "Device"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Device_mac_key" ON "Device"("mac");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_GroupToUser_AB_unique" ON "_GroupToUser"("A", "B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_GroupToUser_B_index" ON "_GroupToUser"("B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_DeviceToUser_AB_unique" ON "_DeviceToUser"("A", "B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_DeviceToUser_B_index" ON "_DeviceToUser"("B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_DeviceToGroup_AB_unique" ON "_DeviceToGroup"("A", "B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_DeviceToGroup_B_index" ON "_DeviceToGroup"("B");
|
||||
@ -1,3 +0,0 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "sqlite"
|
||||
@ -1,38 +0,0 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = "file:../data/db.sqlite"
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
password String
|
||||
admin Boolean @default(false)
|
||||
groups Group[]
|
||||
devices Device[]
|
||||
}
|
||||
|
||||
model Group {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
users User[]
|
||||
devices Device[]
|
||||
}
|
||||
|
||||
model Device {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
mac String @unique
|
||||
broadcast String @default("255.255.255.255")
|
||||
port Int @default(9)
|
||||
packets Int @default(3)
|
||||
users User[]
|
||||
groups Group[]
|
||||
}
|
||||
@ -1,36 +1,39 @@
|
||||
import { prisma } from "$lib/server/db/db";
|
||||
import type { ServerInit } from "@sveltejs/kit";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { nanoid } from "nanoid";
|
||||
import { writeFileSync } from "fs";
|
||||
import { Guard } from "$lib/server/guard";
|
||||
import { getUserFromSession } from "$lib/server/sessions";
|
||||
import { db } from '$lib/server/db';
|
||||
import { Guard } from '$lib/server/guard';
|
||||
import { getUserFromSession } from '$lib/server/sessions';
|
||||
import type { ServerInit } from '@sveltejs/kit';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
export const init: ServerInit = async () => {
|
||||
const anyUser = await prisma.user.findFirst();
|
||||
const anyUser = db.data.users[0];
|
||||
|
||||
if (!anyUser) {
|
||||
const pass = nanoid();
|
||||
if (!anyUser) {
|
||||
const pass = nanoid();
|
||||
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
name: "admin",
|
||||
password: bcrypt.hashSync(pass, 10),
|
||||
admin: true
|
||||
}
|
||||
});
|
||||
await db.update(({ users }) => {
|
||||
users.push({
|
||||
id: nanoid(),
|
||||
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 async function handle({ 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);
|
||||
}
|
||||
|
||||
@ -20,8 +20,10 @@
|
||||
...others
|
||||
}: Props = $props();
|
||||
|
||||
const baseClasses = "block flex items-center text-sm px-4 py-2 transition cursor-pointer rounded box-border";
|
||||
let colorClasses = $state("");
|
||||
let baseClasses = $state("block flex items-center text-sm px-4 py-2 cursor-pointer rounded transition-all duration-300 ease-in-out");
|
||||
|
||||
const defaultColors = "bg-neutral-800 hover:bg-neutral-600 text-white border border-transparent";
|
||||
let colorClasses = $state(defaultColors);
|
||||
|
||||
let fullClasses = $derived(baseClasses + " " + extra + " " + colorClasses);
|
||||
|
||||
@ -35,7 +37,7 @@
|
||||
colorClasses = "bg-emerald-500 hover:bg-emerald-600 text-white border border-transparent";
|
||||
break;
|
||||
default:
|
||||
colorClasses= "bg-neutral-800 hover:bg-neutral-600 text-white border border-transparent";
|
||||
colorClasses= defaultColors;
|
||||
}
|
||||
} else {
|
||||
switch (color) {
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
import { JSONFilePreset } from "lowdb/node";
|
||||
import type { User } from "./types/user";
|
||||
import type { Device } from "./types/device";
|
||||
import type { Group } from "./types/group";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
export const prisma = new PrismaClient();
|
||||
|
||||
type Data = {
|
||||
users: User[],
|
||||
groups: Group[],
|
||||
devices: Device[],
|
||||
};
|
||||
|
||||
const defaultData: Data = {
|
||||
users: [],
|
||||
groups: [],
|
||||
devices: [],
|
||||
};
|
||||
|
||||
//export const db = await JSONFilePreset<Data>("./data/db.json", defaultData);
|
||||
42
src/lib/server/db/index.ts
Normal file
42
src/lib/server/db/index.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { JSONFilePreset } from 'lowdb/node';
|
||||
import type { Group } from './types/group';
|
||||
import type { User } from './types/user';
|
||||
import type { Device } from './types/device';
|
||||
|
||||
type Data = {
|
||||
users: User[];
|
||||
groups: Group[];
|
||||
devices: Device[];
|
||||
};
|
||||
|
||||
export const db = await JSONFilePreset<Data>('db.json', {
|
||||
users: [],
|
||||
groups: [],
|
||||
devices: [],
|
||||
});
|
||||
|
||||
export function getUsersDevices(userId: string): Device[] {
|
||||
const user = db.data.users.find((u) => u.id === userId)!;
|
||||
|
||||
if (user.admin) return db.data.devices;
|
||||
|
||||
const userDevices = db.data.devices.filter((d) => user.devices.includes(d.id));
|
||||
const userGroups = db.data.groups.filter((g) => user.groups.includes(g.id));
|
||||
const groupDevices = userGroups.flatMap((g) =>
|
||||
db.data.devices.filter((d) => g.devices.includes(d.id)),
|
||||
);
|
||||
|
||||
// concat and dedupe
|
||||
return [...new Set([...userDevices, ...groupDevices])];
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
export function assignDefaults<T>(defaults: Partial<T>, obj: any, options = { mutate: false }): T {
|
||||
for (let k of Object.keys(obj)) {
|
||||
if (obj[k] === undefined) {
|
||||
delete obj[k];
|
||||
}
|
||||
}
|
||||
|
||||
return options.mutate ? Object.assign(defaults, obj) : Object.assign({}, defaults, obj);
|
||||
}
|
||||
@ -1,8 +1,14 @@
|
||||
export type Device = {
|
||||
id: string,
|
||||
name: string,
|
||||
mac: string,
|
||||
ip: string,
|
||||
port: number,
|
||||
packets: number
|
||||
}
|
||||
id: string;
|
||||
name: string;
|
||||
mac: string;
|
||||
broadcast: string;
|
||||
port: number;
|
||||
packets: number;
|
||||
};
|
||||
|
||||
export const defaultDevice: Partial<Device> = {
|
||||
broadcast: '255.255.255.255',
|
||||
port: 9,
|
||||
packets: 3,
|
||||
};
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import type { Permission } from "./permission"
|
||||
|
||||
export type Group = {
|
||||
id: string,
|
||||
name: string,
|
||||
permissions: { [key: string]: Permission }
|
||||
}
|
||||
id: string;
|
||||
name: string;
|
||||
devices: string[];
|
||||
};
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
export type Permission = {
|
||||
wake: boolean;
|
||||
}
|
||||
@ -1,11 +1,8 @@
|
||||
import type { Group } from "./group"
|
||||
import type { Permission } from "./permission"
|
||||
|
||||
export type User = {
|
||||
id: string,
|
||||
name: string,
|
||||
password: string,
|
||||
admin: boolean
|
||||
groups: string[],
|
||||
permissions: { [key: string]: Permission }
|
||||
}
|
||||
id: string;
|
||||
name: string;
|
||||
password: string;
|
||||
admin: boolean;
|
||||
groups: string[];
|
||||
devices: string[];
|
||||
};
|
||||
|
||||
@ -1,44 +1,39 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { prisma } from "./db/db";
|
||||
import type { User } from "./db/types/user";
|
||||
import { nanoid } from 'nanoid';
|
||||
import { db } from './db';
|
||||
|
||||
type SessionData = {
|
||||
userId: number,
|
||||
userAgent: string,
|
||||
userId: string;
|
||||
userAgent: string;
|
||||
};
|
||||
|
||||
const sessions: Map<string, SessionData> = 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;
|
||||
}
|
||||
|
||||
const data = sessions.get(sessionId);
|
||||
if (!sessionId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return undefined;
|
||||
}
|
||||
const data = sessions.get(sessionId);
|
||||
|
||||
// what in the nested fuck is this shit
|
||||
// I thought ORMs made it easier but they just make queries more ridiculous
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: data.userId
|
||||
}
|
||||
}) ?? undefined;
|
||||
if (!data) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return user;
|
||||
};
|
||||
// what in the nested fuck is this shit
|
||||
// I thought ORMs made it easier but they just make queries more ridiculous
|
||||
const user = await db.data.users.find((u) => u.id === data.userId);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
export function deleteSession(sessionId?: string) {
|
||||
if (!sessionId) return;
|
||||
sessions.delete(sessionId);
|
||||
}
|
||||
if (!sessionId) return;
|
||||
sessions.delete(sessionId);
|
||||
}
|
||||
|
||||
@ -1,31 +1,9 @@
|
||||
import { prisma } from "$lib/server/db/db";
|
||||
import type { ServerLoad } from "@sveltejs/kit";
|
||||
import { getUsersDevices } from '$lib/server/db';
|
||||
import type { ServerLoad } from '@sveltejs/kit';
|
||||
|
||||
export const load: ServerLoad = async ({ locals: { guard} }) => {
|
||||
const user = guard.requiresAuth().orRedirects().getUser();
|
||||
|
||||
if (user.admin) {
|
||||
return {
|
||||
devices: await prisma.device.findMany(),
|
||||
}
|
||||
}
|
||||
|
||||
const userDevices = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: user.id
|
||||
},
|
||||
include: {
|
||||
devices: true,
|
||||
groups: {
|
||||
include: {
|
||||
devices: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
devices: userDevices == null ? [] :
|
||||
userDevices.devices.concat(userDevices.groups.flatMap(group => group.devices)),
|
||||
}
|
||||
};
|
||||
export const load: ServerLoad = async ({ locals: { guard } }) => {
|
||||
const user = guard.requiresAuth().orRedirects().getUser();
|
||||
return {
|
||||
devices: getUsersDevices(user.id),
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,141 +1,115 @@
|
||||
import { prisma } from '$lib/server/db/db';
|
||||
import { db, getUsersDevices } from '$lib/server/db/index.js';
|
||||
import { fail, redirect, type ServerLoad } from '@sveltejs/kit';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { wake } from 'wake_on_lan';
|
||||
|
||||
export const load: ServerLoad = async ({ locals: { guard }, params }) => {
|
||||
guard.requiresAdmin().orRedirects();
|
||||
guard.requiresAdmin().orRedirects();
|
||||
|
||||
const device = await prisma.device.findUnique({
|
||||
where: {
|
||||
id: parseInt(params.slug!) || -1,
|
||||
}
|
||||
});
|
||||
const device = db.data.devices.find((d) => d.id === params.slug);
|
||||
|
||||
if (!device && params.slug !== "new") {
|
||||
redirect(302, "/dashboard/devices");
|
||||
}
|
||||
if (!device && params.slug !== 'new') {
|
||||
redirect(302, '/dashboard/devices');
|
||||
}
|
||||
|
||||
return {
|
||||
device,
|
||||
}
|
||||
}
|
||||
return {
|
||||
device,
|
||||
};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
update: async ({ request, cookies, params, locals: { guard } }) => {
|
||||
if (guard.requiresAdmin().isFailed()) {
|
||||
return fail(403);
|
||||
}
|
||||
update: async ({ request, cookies, params, locals: { guard } }) => {
|
||||
if (guard.requiresAdmin().isFailed()) {
|
||||
return fail(403);
|
||||
}
|
||||
|
||||
const form = await request.formData();
|
||||
const name = form.get("name")?.toString();
|
||||
const mac = form.get("mac")?.toString();
|
||||
const broadcast = form.get("broadcast")?.toString();
|
||||
const port = form.get("port")?.toString();
|
||||
const packets = form.get("packets")?.toString();
|
||||
const form = await request.formData();
|
||||
const name = form.get('name')?.toString();
|
||||
const mac = form.get('mac')?.toString();
|
||||
const broadcast = form.get('broadcast')?.toString();
|
||||
const port = form.get('port')?.toString();
|
||||
const packets = form.get('packets')?.toString();
|
||||
|
||||
if (!name || !mac) {
|
||||
// TODO better validation
|
||||
return {
|
||||
error: "MISSING_FIELDS"
|
||||
}
|
||||
}
|
||||
if (!name || !mac) {
|
||||
// TODO better validation
|
||||
return {
|
||||
error: 'MISSING_FIELDS',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
if (params.slug === "new") {
|
||||
await prisma.device.create({
|
||||
data: {
|
||||
name,
|
||||
mac,
|
||||
broadcast,
|
||||
port: port ? parseInt(port) : undefined,
|
||||
packets: packets ? parseInt(packets) : undefined
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await prisma.device.update({
|
||||
where: {
|
||||
id: parseInt(params.slug)
|
||||
},
|
||||
data: {
|
||||
name,
|
||||
mac,
|
||||
broadcast,
|
||||
port: port ? parseInt(port) : undefined,
|
||||
packets: packets ? parseInt(packets) : undefined
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.code === "P2002") {
|
||||
return fail(409, { error: "This name or this MAC adress is already in use. Please make sure they are unique." });
|
||||
} else {
|
||||
console.error(e);
|
||||
return fail(500, { error: "DATABASE_ERROR" });
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (params.slug === 'new') {
|
||||
await db.update(({ devices }) => {
|
||||
devices.push({
|
||||
id: nanoid(),
|
||||
name,
|
||||
mac,
|
||||
broadcast: broadcast ?? '255.255.255.255',
|
||||
port: port ? parseInt(port) : 9,
|
||||
packets: packets ? parseInt(packets) : 3,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
await db.update(({ devices }) => {
|
||||
let dev = devices.find((d) => d.id === params.slug);
|
||||
|
||||
redirect(302, "/dashboard/devices");
|
||||
},
|
||||
delete: async ({ locals: { guard }, params }) => {
|
||||
if (guard.requiresAdmin().isFailed()) {
|
||||
return fail(403);
|
||||
}
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
await prisma.device.delete({
|
||||
where: {
|
||||
id: parseInt(params.slug)
|
||||
}
|
||||
});
|
||||
dev.name = name;
|
||||
dev.mac = mac;
|
||||
dev.broadcast = broadcast ?? dev.broadcast;
|
||||
dev.port = port ? parseInt(port) : dev.port;
|
||||
dev.packets = packets ? parseInt(packets) : dev.packets;
|
||||
});
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.code === 'P2002') {
|
||||
return fail(409, {
|
||||
error:
|
||||
'This name or this MAC adress is already in use. Please make sure they are unique.',
|
||||
});
|
||||
} else {
|
||||
console.error(e);
|
||||
return fail(500, { error: 'DATABASE_ERROR' });
|
||||
}
|
||||
}
|
||||
|
||||
redirect(302, "/dashboard/devices");
|
||||
},
|
||||
wake: async ({ params, locals: { guard } }) => {
|
||||
console.log("Trying to wake " + params.slug);
|
||||
redirect(302, '/dashboard/devices');
|
||||
},
|
||||
delete: async ({ locals: { guard }, params }) => {
|
||||
if (guard.requiresAdmin().isFailed()) {
|
||||
return fail(403);
|
||||
}
|
||||
|
||||
guard = guard.requiresAuth();
|
||||
db.data.devices = db.data.devices.filter((d) => d.id !== params.slug);
|
||||
db.write();
|
||||
|
||||
if (guard.isFailed()) {
|
||||
console.log("Failed guard");
|
||||
return fail(403);
|
||||
}
|
||||
redirect(302, '/dashboard/devices');
|
||||
},
|
||||
wake: async ({ params, locals: { guard } }) => {
|
||||
console.log('Trying to wake ' + params.slug);
|
||||
|
||||
const userDevices = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: guard.getUser().id
|
||||
},
|
||||
include: {
|
||||
devices: true,
|
||||
groups: {
|
||||
include: {
|
||||
devices: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
guard = guard.requiresAuth();
|
||||
|
||||
if (!userDevices) {
|
||||
console.log("Failed to find user devices");
|
||||
return fail(403);
|
||||
}
|
||||
if (guard.isFailed()) {
|
||||
console.log('Failed guard');
|
||||
return fail(403);
|
||||
}
|
||||
|
||||
let deviceId = parseInt(params.slug);
|
||||
const device = getUsersDevices(guard.getUser().id).find((d) => d.id === params.slug);
|
||||
|
||||
if (isNaN(deviceId)) {
|
||||
return fail(400);
|
||||
}
|
||||
if (!device) {
|
||||
return fail(404);
|
||||
}
|
||||
|
||||
const device = userDevices.devices.find(d => d.id === deviceId) ?? userDevices.groups.flatMap(g => g.devices).find(d => d.id === deviceId);
|
||||
console.log('Trying to wake ' + device.name);
|
||||
|
||||
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
|
||||
}, () => {});
|
||||
}
|
||||
}
|
||||
wake(device.mac, {
|
||||
address: device.broadcast,
|
||||
port: device.port,
|
||||
num_packets: device.packets,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import { prisma } from "$lib/server/db/db";
|
||||
import { type ServerLoad } from "@sveltejs/kit";
|
||||
import { db } from '$lib/server/db';
|
||||
import { type ServerLoad } from '@sveltejs/kit';
|
||||
|
||||
export const load: ServerLoad = async ({ locals: { guard } }) => {
|
||||
guard.requiresAdmin().orRedirects();
|
||||
|
||||
return {
|
||||
groups: await prisma.group.findMany({
|
||||
include: {
|
||||
users: true,
|
||||
devices: true,
|
||||
}
|
||||
}),
|
||||
}
|
||||
};
|
||||
guard.requiresAdmin().orRedirects();
|
||||
return {
|
||||
groups: db.data.groups,
|
||||
userCounts: db.data.groups.reduce(
|
||||
(acc, group) => {
|
||||
acc[group.id] = db.data.users.filter((u) => u.groups.includes(group.id)).length;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import ResourceCard from "$lib/components/resources/ResourceCard.svelte";
|
||||
import ResourceListPage from "$lib/components/resources/ResourceListPage.svelte";
|
||||
import IconPlus from "~icons/tabler/plus";
|
||||
import IconEdit from "~icons/tabler/edit";
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import IconEdit from "~icons/tabler/edit";
|
||||
import IconPlus from "~icons/tabler/plus";
|
||||
|
||||
let { data } = $props();
|
||||
</script>
|
||||
@ -19,7 +19,7 @@
|
||||
{:else}
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
{#each data.groups as group}
|
||||
<ResourceCard title={group.name} subtitle="{group.users.length} users, {group.devices.length} devices">
|
||||
<ResourceCard title={group.name} subtitle="{data.userCounts[group.id]} users, {group.devices.length} devices">
|
||||
{#snippet actionsSnippet()}
|
||||
{#if data.user.admin}
|
||||
<Button Icon={IconEdit} a href="/dashboard/groups/{group.id}" extra="!p-2"/>
|
||||
|
||||
@ -1,81 +1,71 @@
|
||||
import { prisma } from "$lib/server/db/db";
|
||||
import { fail, redirect, type Actions, type ServerLoad } from "@sveltejs/kit";
|
||||
import { db } from '$lib/server/db';
|
||||
import { fail, redirect, type Actions, type ServerLoad } from '@sveltejs/kit';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
export const load: ServerLoad = async ({ locals: { guard },params }) => {
|
||||
guard.requiresAdmin().orRedirects();
|
||||
export const load: ServerLoad = async ({ locals: { guard }, params }) => {
|
||||
guard.requiresAdmin().orRedirects();
|
||||
|
||||
const group = await prisma.group.findUnique({
|
||||
where: {
|
||||
id: parseInt(params.slug!) || -1,
|
||||
}, include: {
|
||||
devices: true
|
||||
}
|
||||
});
|
||||
const group = db.data.groups.find((g) => g.id === params.slug);
|
||||
|
||||
if (!group && params.slug !== "new") {
|
||||
redirect(302, "/dashboard/groups");
|
||||
}
|
||||
if (!group && params.slug !== 'new') {
|
||||
redirect(302, '/dashboard/groups');
|
||||
}
|
||||
|
||||
return {
|
||||
devices: await prisma.device.findMany(),
|
||||
group,
|
||||
}
|
||||
return {
|
||||
devices: db.data.devices,
|
||||
group,
|
||||
};
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
update: async ({ request, locals: { guard }, params }) => {
|
||||
if (guard.requiresAdmin().isFailed()) {
|
||||
return fail(403);
|
||||
}
|
||||
update: async ({ request, locals: { guard }, params }) => {
|
||||
if (guard.requiresAdmin().isFailed()) {
|
||||
return fail(403);
|
||||
}
|
||||
|
||||
const form = await request.formData();
|
||||
const name = form.get("name")?.toString();
|
||||
const devices = form.getAll("devices").map(d => ({ id: parseInt(d.toString()) }));
|
||||
const form = await request.formData();
|
||||
const name = form.get('name')?.toString();
|
||||
const devices = form.getAll('devices').map((d) => d.toString());
|
||||
|
||||
if (!name) {
|
||||
// TODO better validation
|
||||
return {
|
||||
error: "MISSING_FIELDS"
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
// TODO better validation
|
||||
return {
|
||||
error: 'MISSING_FIELDS',
|
||||
};
|
||||
}
|
||||
|
||||
if (params.slug === "new") {
|
||||
await prisma.group.create({
|
||||
data: {
|
||||
name,
|
||||
devices: {
|
||||
connect: devices
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await prisma.group.update({
|
||||
where: {
|
||||
id: parseInt(params.slug!) || -1,
|
||||
},
|
||||
data: {
|
||||
name,
|
||||
devices: {
|
||||
set: devices
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (params.slug === 'new') {
|
||||
await db.update(({ groups }) => {
|
||||
groups.push({
|
||||
id: nanoid(),
|
||||
name,
|
||||
devices,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
await db.update(({ groups }) => {
|
||||
let group = groups.find((g) => g.id === params.slug);
|
||||
|
||||
redirect(302, "/dashboard/groups");
|
||||
},
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete: async ({ locals: { guard }, params }) => {
|
||||
if (guard.requiresAdmin().isFailed()) {
|
||||
return fail(403);
|
||||
}
|
||||
group.name = name;
|
||||
group.devices = devices;
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.group.delete({
|
||||
where: {
|
||||
id: parseInt(params.slug!) || -1,
|
||||
}
|
||||
});
|
||||
redirect(302, '/dashboard/groups');
|
||||
},
|
||||
|
||||
redirect(302, "/dashboard/groups");
|
||||
}
|
||||
};
|
||||
delete: async ({ locals: { guard }, params }) => {
|
||||
if (guard.requiresAdmin().isFailed()) {
|
||||
return fail(403);
|
||||
}
|
||||
|
||||
db.data.groups = db.data.groups.filter((g) => g.id !== params.slug);
|
||||
db.write();
|
||||
|
||||
redirect(302, '/dashboard/groups');
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import InputSelect from "$lib/components/forms/InputSelect.svelte";
|
||||
import InputText from "$lib/components/forms/InputText.svelte";
|
||||
import ResourceEditPage from "$lib/components/resources/ResourceEditPage.svelte";
|
||||
import IconDeviceFloppy from "~icons/tabler/device-floppy";
|
||||
import IconTrash from "~icons/tabler/trash";
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import IconDeviceFloppy from "~icons/tabler/device-floppy";
|
||||
import IconTrash from "~icons/tabler/trash";
|
||||
|
||||
let { form, data } = $props();
|
||||
</script>
|
||||
@ -25,7 +25,7 @@
|
||||
data={data.devices.map(d => ({
|
||||
value: d.id.toString(),
|
||||
name: d.name,
|
||||
selected: data.group?.devices.find(gd => gd.id === d.id) ? true : false }))}/>
|
||||
selected: data.group?.devices.find(gd => gd === d.id) ? true : false }))}/>
|
||||
|
||||
<div class="flex gap-5 items-center">
|
||||
<Button Icon={IconDeviceFloppy}>{data.group ? "Update" : "Create"}</Button>
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import { prisma } from "$lib/server/db/db";
|
||||
import type { ServerLoad } from "@sveltejs/kit";
|
||||
import { db } from '$lib/server/db';
|
||||
import type { User } 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 prisma.user.findMany({
|
||||
include: {
|
||||
groups: true,
|
||||
devices: true
|
||||
}
|
||||
}),
|
||||
}
|
||||
};
|
||||
return {
|
||||
users: db.data.users.map((u) => {
|
||||
let safeUser = structuredClone(u) as Partial<User>;
|
||||
delete safeUser['password'];
|
||||
|
||||
return safeUser;
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,88 +1,91 @@
|
||||
import { prisma } from "$lib/server/db/db";
|
||||
import { fail, redirect, type Actions } from "@sveltejs/kit";
|
||||
import bcrypt from "bcryptjs";
|
||||
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';
|
||||
|
||||
export const load = async ({ locals: { guard }, params }) => {
|
||||
guard.requiresAdmin().orRedirects();
|
||||
guard.requiresAdmin().orRedirects();
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: parseInt(params.slug!) || -1,
|
||||
},
|
||||
include: {
|
||||
groups: true,
|
||||
devices: true
|
||||
}
|
||||
});
|
||||
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 && params.slug !== 'new') {
|
||||
redirect(302, '/dashboard/users');
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
groups: await prisma.group.findMany(),
|
||||
devices: await prisma.device.findMany(),
|
||||
}
|
||||
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);
|
||||
}
|
||||
update: async ({ request, locals: { guard }, params }) => {
|
||||
if (guard.requiresAdmin().isFailed()) {
|
||||
return fail(403);
|
||||
}
|
||||
|
||||
const form = await request.formData();
|
||||
const name = form.get("name")?.toString();
|
||||
const admin = form.get("admin")?.toString() === "on";
|
||||
const password = form.get("password")?.toString() ?? "";
|
||||
const groups = form.getAll("groups").map(g => ({ id: parseInt(g.toString()) }));
|
||||
const devices = form.getAll("devices").map(d => ({ id: parseInt(d.toString()) }));
|
||||
const form = await request.formData();
|
||||
const name = form.get('name')?.toString();
|
||||
const admin = form.get('admin')?.toString() === 'on';
|
||||
const password = form.get('password')?.toString() ?? '';
|
||||
const groups = form.getAll('groups').map((g) => g.toString());
|
||||
const devices = form.getAll('devices').map((d) => d.toString());
|
||||
|
||||
if (!name) {
|
||||
// TODO better validation
|
||||
return {
|
||||
error: "MISSING_FIELDS"
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
// TODO better validation
|
||||
return {
|
||||
error: 'MISSING_FIELDS',
|
||||
};
|
||||
}
|
||||
|
||||
if (params.slug === "new") {
|
||||
if (password.length < 4) {
|
||||
return {
|
||||
error: "PASSWORD_TOO_WEAK"
|
||||
}
|
||||
}
|
||||
if (params.slug === 'new') {
|
||||
if (password.length < 4) {
|
||||
return {
|
||||
error: 'PASSWORD_TOO_WEAK',
|
||||
};
|
||||
}
|
||||
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
name,
|
||||
password: bcrypt.hashSync(password, 10),
|
||||
admin,
|
||||
groups: {
|
||||
connect: groups
|
||||
},
|
||||
devices: {
|
||||
connect: devices
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
id: parseInt(params.slug!) || -1,
|
||||
},
|
||||
data: {
|
||||
name,
|
||||
admin,
|
||||
groups: {
|
||||
set: groups
|
||||
},
|
||||
devices: {
|
||||
set: devices
|
||||
},
|
||||
password: password.length > 0 ? bcrypt.hashSync(password, 10) : undefined
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
await db.update(({ users }) => {
|
||||
users.push({
|
||||
id: nanoid(),
|
||||
name,
|
||||
admin,
|
||||
groups,
|
||||
devices,
|
||||
password: bcrypt.hashSync(password, 10),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
await db.update(({ users }) => {
|
||||
let user = users.find((u) => u.id === params.slug);
|
||||
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
user.name = name;
|
||||
user.admin = admin;
|
||||
user.groups = groups;
|
||||
user.devices = devices;
|
||||
if (password.length > 0) {
|
||||
user.password = bcrypt.hashSync(password, 10);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
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();
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import InputSelect from "$lib/components/forms/InputSelect.svelte";
|
||||
import InputText from "$lib/components/forms/InputText.svelte";
|
||||
import ResourceEditPage from "$lib/components/resources/ResourceEditPage.svelte";
|
||||
import IconDeviceFloppy from "~icons/tabler/device-floppy";
|
||||
import IconTrash from "~icons/tabler/trash";
|
||||
import InputToggle from "$lib/components/forms/InputToggle.svelte";
|
||||
import { enhance } from "$app/forms";
|
||||
import InputSelect from "$lib/components/forms/InputSelect.svelte";
|
||||
import InputText from "$lib/components/forms/InputText.svelte";
|
||||
import InputToggle from "$lib/components/forms/InputToggle.svelte";
|
||||
import ResourceEditPage from "$lib/components/resources/ResourceEditPage.svelte";
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import IconDeviceFloppy from "~icons/tabler/device-floppy";
|
||||
import IconTrash from "~icons/tabler/trash";
|
||||
|
||||
let { data, form } = $props();
|
||||
</script>
|
||||
@ -39,7 +39,7 @@
|
||||
data={data.groups.map(g => ({
|
||||
value: g.id.toString(),
|
||||
name: g.name,
|
||||
selected: data.user?.groups.find(ug => ug.id === g.id) ? true : false }))}/>
|
||||
selected: data.user?.groups?.find(ug => ug === g.id) ? true : false }))}/>
|
||||
|
||||
<InputSelect
|
||||
label="Devices"
|
||||
@ -48,7 +48,7 @@
|
||||
data={data.devices.map(d => ({
|
||||
value: d.id.toString(),
|
||||
name: d.name,
|
||||
selected: data.user?.devices.find(ud => ud.id === d.id) ? true : false }))}/>
|
||||
selected: data.user?.devices?.find(ud => ud === d.id) ? true : false }))}/>
|
||||
|
||||
<div class="flex gap-5 items-center">
|
||||
<Button Icon={IconDeviceFloppy}>{data.user ? "Update" : "Create"}</Button>
|
||||
|
||||
@ -1,48 +1,48 @@
|
||||
import { db } from '$lib/server/db';
|
||||
import { createSession, getUserFromSession } from '$lib/server/sessions';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { Actions } from './$types';
|
||||
import { prisma } from '$lib/server/db/db';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import type { Actions } from './$types';
|
||||
|
||||
export const actions = {
|
||||
default: async ({ cookies, request }) => {
|
||||
if (await getUserFromSession(cookies.get('session'))) {
|
||||
redirect(302, "/dashboard");
|
||||
}
|
||||
redirect(302, '/dashboard');
|
||||
}
|
||||
|
||||
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 {
|
||||
error: "MISSING_CREDENTIALS"
|
||||
}
|
||||
}
|
||||
if (!username || !password) {
|
||||
return {
|
||||
error: 'MISSING_CREDENTIALS',
|
||||
};
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
name: username
|
||||
}
|
||||
});
|
||||
const user = db.data.users.find((u) => u.name === username);
|
||||
|
||||
if (!user || !bcrypt.compareSync(password, user.password)) {
|
||||
return {
|
||||
error: "INVALID_CREDENTIALS"
|
||||
}
|
||||
}
|
||||
if (!user || !bcrypt.compareSync(password, user.password)) {
|
||||
return {
|
||||
error: 'INVALID_CREDENTIALS',
|
||||
};
|
||||
}
|
||||
|
||||
cookies.set("session", createSession({
|
||||
userAgent: request.headers.get("user-agent") ?? "UNKNOWN",
|
||||
userId: user.id
|
||||
}), {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: true,
|
||||
maxAge: 60 * 60 * 24,
|
||||
});
|
||||
cookies.set(
|
||||
'session',
|
||||
createSession({
|
||||
userAgent: request.headers.get('user-agent') ?? 'UNKNOWN',
|
||||
userId: user.id,
|
||||
}),
|
||||
{
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: true,
|
||||
maxAge: 60 * 60 * 24,
|
||||
},
|
||||
);
|
||||
|
||||
redirect(302, "/dashboard");
|
||||
}
|
||||
} satisfies Actions;
|
||||
redirect(302, '/dashboard');
|
||||
},
|
||||
} satisfies Actions;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user