Compare commits

..

No commits in common. "170e8a8c4fa5f8f8415afdc4c72fbb75217f187a" and "503c86a84b4c26bb4dbd9743db8818ecda5d9c43" have entirely different histories.

5 changed files with 46 additions and 156 deletions

View File

@ -6,7 +6,7 @@
"position": 0,
"name": "Anzeigen"
},
"CREATE": {
"ADD": {
"position": 1,
"name": "Anlegen"
},
@ -25,7 +25,7 @@
},
"meta_permissions": {
"MANAGE": {
"permissions": ["VIEW", "CREATE", "DELETE", "EDIT"],
"permissions": ["VIEW", "ADD", "DELETE", "EDIT"],
"name": "Verwalten"
}
}

View File

@ -15,40 +15,17 @@ const CHECK_QUERY: string =
const USER_DATABASE_SETUP: string[] = [
"PRAGMA foreign_keys = ON;",
"CREATE TABLE meta (key TEXT PRIMARY KEY NOT NULL, value NUMBER);",
"INSERT INTO meta(key, value) VALUES ('triggerActive', 1);",
`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name TEXT,
gender TEXT,
address TEXT,
username TEXT UNIQUE,
username TEXT,
password TEXT,
permissions INTEGER DEFAULT 0,
created DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL
);`,
`CREATE TABLE IF NOT EXISTS users_history (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
user_id INTEGER,
name TEXT,
gender TEXT,
address TEXT,
username TEXT,
permissions INTEGER DEFAULT 0,
created DATETIME NOT NULL,
deleted DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);`,
`CREATE TRIGGER user_delete_history
BEFORE DELETE ON users
WHEN (SELECT value FROM meta WHERE key = 'triggerActive') = 1
BEGIN
INSERT INTO users_history(user_id, name, gender, address, username, permissions, created) VALUES (OLD.id, OLD.name, OLD.gender, OLD.address, OLD.username, OLD.permissions, OLD.created);
END;`,
`CREATE TABLE refresh_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
user_id INTEGER NOT NULL,
@ -88,9 +65,6 @@ const USER_DATABASE_EMPTY: string =
const USER_DATABASE_UPDATE_PASSWORD: string =
"UPDATE users SET password=$password WHERE id=$id;"
const USER_DATABASE_REMOVE_USER: string =
"DELETE FROM users WHERE id = $id;"
/*const USER_DATABASE_ADD_ACCESS_TOKEN: string =
"INSERT INTO access_tokens (user_id, token, expiry_date) VALUES ($user_id, $token, $expiry_date);"
@ -101,7 +75,7 @@ const ENTRY_DATABASE_SETUP: string[] = [
"PRAGMA foreign_keys = ON;",
"CREATE TABLE meta (key TEXT PRIMARY KEY NOT NULL, value NUMBER);",
"INSERT INTO meta(key, value) VALUES ('triggerActive', 1);",
"INSERT INTO meta(key, value) VALUES ('triggerActive', 1)",
`CREATE TABLE records (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
@ -118,7 +92,7 @@ const ENTRY_DATABASE_SETUP: string[] = [
record_id INTEGER NOT NULL,
date VARCHAR(10),
start VARCHAR(5),
end VARCHAR(5),
end V<F52>ARCHAR(5),
comment TEXT,
modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY(record_id) REFERENCES records(id)
@ -258,9 +232,9 @@ export class User {
}
}
get_months(): { year: number, month: number }[] {
get_months(): { year: string, month: string }[] {
const query = this._database.query(ENTRY_DATABASE_GET_MONTHS);
const res = query.all() as { year: string, month: string }[];
const res = query.all();
const ret = res.map((v) => { return { year: toInt(v.year), month: toInt(v.month) }})
@ -269,7 +243,7 @@ export class User {
get_quarters(): { year: number, quarter: number }[] {
const query = this._database.query(ESTIMATES_DATABASE_GET_QUARTERS)
const res = query.all() as { year: number, quarter: number }[];
const res = query.all();
return res;
}
@ -295,14 +269,14 @@ export class User {
}
const query = this._database.query(ENTRY_DATABASE_GET_ENTRIES_IN_MONTH);
const res = query.all({ year: year.toString(), month: month.toString().padStart(2, '0') }) as RecordEntry[];
const res = query.all({ year: year.toString(), month: month.toString().padStart(2, '0') });
return res;
}
get_entry(id: number): RecordEntry {
const query = this._database.query(ENTRY_DATABASE_GET_ENTRY_BY_ID);
const res = query.get({ id: id }) as RecordEntry;
const res = query.get({ id: id });
return res;
}
@ -344,23 +318,23 @@ export class User {
get_estimates(): Array<EstimatesEntry> {
const query = this._database.query(ESTIMATES_DATABASE_GET_ALL);
const res = query.all() as EstimatesEntry[];
const res = query.all();
return res;
}
get_estimate(year: number, quarter: number): EstimatesEntry {
const query = this._database.query(ESTIMATES_DATABASE_GET_QUART);
const res = query.get({ year: year, quarter: quarter }) as EstimatesEntry;
const res = query.get({ year: year, quarter: quarter });
return res;
}
get_estimate_by_month(year: number, month: number): number {
const query = this._database.query(ESTIMATES_DATABASE_GET_QUART);
const res = query.get({ year: year, quarter: Math.floor(month / 4 + 1) }) as EstimatesEntry;
const res = query.all({ year: year, quarter: Math.floor(month / 4 + 1) });
return res?.[`estimate_${month % 3}`] ?? NaN;
return res[0]?.[`estimate_${month % 3}`] ?? NaN;
}
insert_estimate(year: number, quarter: number, estimate_0: number, estimate_1: number, estimate_2: number) {
@ -370,6 +344,7 @@ export class User {
const query = this._database.query(ESTIMATES_DATABASE_INSERT);
const res = query.run({ year: year, quarter: quarter, estimate_0: estimate_0, estimate_1: estimate_1, estimate_2: estimate_2 });
console.log(res)
return res.changes > 1;
}
@ -390,6 +365,8 @@ function is_db_initialized(db: Database): boolean {
throw exception;
}
console.log(exception);
return false;
}
}
@ -399,9 +376,7 @@ function get_user_db_name(user_id: number) {
}
function setup_db(db: Database, setup_queries: string[]) {
db.transaction(() => {
setup_queries.forEach((q) => { db.query(q).run(); });
})()
setup_queries.forEach((q) => { /*console.log(q);*/ db.query(q).run(); });
}
export async function init_db() {
@ -422,15 +397,23 @@ export function close_db() {
}
}
export async function create_user(user: { name: string, gender: string, address: string, username: string, password: string }): Promise<number | bigint> {
export async function create_user(user: { name: string, gender: string, address: string, username: string, password: string }): Promise<boolean> {
user.password = await Bun.password.hash(user.password, { algorithm: "bcrypt", cost: 11});
const statement = user_database.query(USER_DATABASE_ADD_USER);
const result = statement.run(user);
try {
const statement = user_database.query(USER_DATABASE_ADD_USER);
const result = statement.run(user);
return result.lastInsertRowid;
return result.changes == 1;
} catch (e) {
console.log(e);
if (e instanceof SQLiteError) {
return false;
}
throw e;
}
}
export function get_all_user(): { id: number, username: string, name: string }[] {
@ -471,7 +454,7 @@ export function get_user_by_name(username: string): User | null {
fs.mkdir(DATABASES_PATH, { recursive: true });
let userdb = new Database(get_user_db_name(user.id), { create: true, strict: true });
if (!is_db_initialized(userdb)) {
setup_db(userdb, ENTRY_DATABASE_SETUP);
}
@ -516,13 +499,6 @@ export function update_user_password(user_id: number, password: string) {
return result.changes > 0
}
export function remove_user(user_id: number) {
const query = user_database.prepare(USER_DATABASE_REMOVE_USER)
const result = query.run({ id: user_id })
return result.changes > 0
}
type ExportEstimatesEntry = Pick<EstimatesEntry, Exclude<keyof EstimatesEntry, "id" | "created">>
type ExportRecordsEntry = Pick<RecordEntry, Exclude<keyof RecordEntry, "id" | "created">>

View File

@ -2,15 +2,14 @@ import type { PageServerLoad, Actions } from "./$types"
import type { UserEntry } from "$lib/db_types"
import { SQLiteError } from "bun:sqlite"
import { fail, redirect } from "@sveltejs/kit"
import { fail } from "@sveltejs/kit"
import Permissions from "$lib/permissions"
import { toInt } from "$lib/util"
import Logs from "$lib/server/log"
import SessionStore from "$lib/server/session_store"
import { get_user_entry_by_id, updateUser, import_user_data, create_user, remove_user } from "$lib/server/database"
import { get_user_entry_by_id, updateUser, import_user_data } from "$lib/server/database"
import { change_password } from "$lib/server/auth"
export const load: PageServerLoad = ({ locals, url }) => {
@ -21,15 +20,7 @@ export const load: PageServerLoad = ({ locals, url }) => {
let user: UserEntry|null = locals.user.toUserEntry()
const target_user = url.searchParams.get("user") as string|null
if (target_user == "new") {
return {
user: null
}
}
if (locals.user.id != (toInt(target_user ?? locals.user.id.toFixed(0)))) {
if (locals.user.id != (toInt(url.searchParams.get("user") ?? locals.user.id.toFixed(0)))) {
if (!Permissions.has(locals.user.permissions, Permissions.USERADMIN.VIEW)) {
return fail(403, { message: "Insufficient Permissions" })
}
@ -67,7 +58,7 @@ export const actions = {
const data = await request.formData();
const id = data.get("id") == "new" ? -1 : toInt((data.get("id") as string|null) ?? "NaN")
const id = toInt((data.get("id") as string|null) ?? "NaN")
const name = data.get("name") as string|null
const gender = data.get("gender") as string|null
const address = data.get("address") as string|null
@ -82,39 +73,6 @@ export const actions = {
return fail(400, { message: "invalid request" })
}
if (id == -1) {
if (!Permissions.has(locals.user.permissions, Permissions.USERADMIN.CREATE)) {
return fail(403, { message: "Keine Berechtigung" })
}
if (username.length == 0) {
return fail(400, { message: "Benutzername muss angegeben werden" })
}
if (password1 == null || password2 == null || password1.length == 0 || password2.length == 0) {
return fail(400, { message: "Passwort muss für einen neuen Benutzer angegeben werden" })
}
if (password1 != password2) {
return fail(400, { message: "Passwörter müssen übereinstimmen" })
}
let new_user: number | bigint = -1
try {
new_user = await create_user({ name, gender, address, username, password: password1 })
} catch (e) {
if (e instanceof SQLiteError && e.code == "SQLITE_CONSTRAINT_UNIQUE") {
return fail(400, { message: "Benutzername ist bereits vergeben" })
}
throw e
}
if (new_user < 0) {
return fail(500, { message: "Interner Fehler" })
}
return redirect(303, `/user?user=${new_user}`)
}
if (locals.user.id != id
&& (!Permissions.has(locals.user.permissions, Permissions.USERADMIN.EDIT)
|| ((password1 != null || password2 != null) && !Permissions.has(locals.user.permissions, Permissions.USERADMIN.ADMIN)))) {
@ -125,7 +83,6 @@ export const actions = {
if (password1 != password2) {
return fail(400, { message: "Passwörter müssen übereinstimmen" })
}
const result = change_password(id, password1)
if (!result) {
return fail(500, { message: "Database failure"})
@ -197,35 +154,5 @@ export const actions = {
}
return { message: "Import abgeschlossen"}
},
delete: async ({ locals, request }) => {
if (!locals.user) {
return fail(401, { message: "Unauthorized User" })
}
const data = await request.formData()
const user_id = toInt((data.get("user") as string|null) ?? "NaN")
if (isNaN(user_id)) {
return fail(400, { message: "Bad request" })
}
if (!Permissions.has(locals.user.permissions, Permissions.USERADMIN.DELETE)) {
Logs.user.warn(`User ${locals.user.id} tried to delete user ${id} without sufficient permissions`)
return fail(403, { message: "Unzureichend Berechtigung" })
}
const result = remove_user(user_id)
if (!result) {
return fail(500, { message: "Fehler beim Löschen" })
}
if (Permissions.has(locals.user.permissions, Permissions.USERADMIN.VIEW)) {
return redirect(303, "/useradmin")
}
return { message: "Erfolgreich gelöscht" }
}
} satisfies Actions

View File

@ -17,16 +17,12 @@
</script>
<form method="GET" id="form_export" action={`/user/export?${page.url.searchParams.toString()}`} ></form>
<form method="POST" id="form_import" action="?/import" enctype="multipart/form-data"><input type="hidden" name="id" value={data.user?.id ?? "new"} /></form>
<form method="POST" id="form_delete" action="?/delete" onsubmit={(event) => {
if (!confirm(`Sicher ${data.user?.name} zu löschen?`)) {
event.preventDefault()
}}}></form>
<form method="POST" id="form_import" action="?/import" enctype="multipart/form-data"><input type="hidden" name="id" value={data.user.id} /></form>
<form method="POST" id="form_edit" action={`?/edit&${page.url.searchParams.toString()}`} use:enhance={() => {
return async ({update}) => { update({ reset: false }) }
}}>
<input type="hidden" name="id" value={data.user?.id ?? "new"} />
<input type="hidden" name="id" value={data.user.id} />
<div class="root">
@ -45,7 +41,7 @@
<tbody>
<tr>
<td>Name</td>
<td><input type="text" name="name" value={data.user?.name} /></td>
<td><input type="text" name="name" value={data.user.name} /></td>
</tr>
<tr>
<td>Geschlecht</td>
@ -56,7 +52,7 @@
</tr>
<tr>
<td>Addresse</td>
<td><input type="text" name="address" value={data.user?.address} /></td>
<td><input type="text" name="address" value={data.user.address} /></td>
</tr>
</tbody>
</table>
@ -73,7 +69,7 @@
<tbody>
<tr>
<td>Benutzername</td>
<td colspan="2"><input type="text" name="username" value={data.user?.username} /></td>
<td colspan="2"><input type="text" name="username" value={data.user.username} /></td>
</tr>
<tr>
<td>Passwort ändern</td>
@ -107,7 +103,7 @@
type="checkbox"
name="USERADMIN"
value={permission.value}
checked={Permissions.has(data.user?.permissions ?? 0, permission.value)}
checked={Permissions.has(data.user.permissions, permission.value)}
disabled={disabled}
data-bits={Permissions.deconstruct(permission.value).join(" ")}
onclick={(event) => {
@ -144,6 +140,8 @@
}
}
}} />
{permission.name}
</label>
@ -156,7 +154,7 @@
{/if}
<button class="save-button" type="submit">{data.user ? "Speichern" : "Anlegen"}</button>
<button class="save-button" type="submit">Speichern</button>
<table>
<colgroup>
@ -184,9 +182,6 @@
</tbody>
</table>
{#if data.user}
<button class="delete_button" form="form_delete" name="user" value={data.user?.id}>Löschen</button>
{/if}
</div>
</form>
@ -258,8 +253,4 @@ input[type="file"] {
text-align: center;
}
.delete_button {
margin-top: 50px;
float: right;
}
</style>

View File

@ -3,14 +3,13 @@
import type { PageProps } from "./$types"
import { enhance } from "$app/forms";
import Permissions from "$lib/permissions"
const { data }: PageProps = $props();
console.log(data)
</script>
<form method="GET" id="form_manage_user" action="user"></form>
<form method="GET" id="form_add_user" action="user"></form>
<div>
<h1>Benutzerverwaltung</h1>
@ -21,9 +20,6 @@
{/each}
</select>
<button type="submit" form="form_manage_user">Edit</button>
{#if Permissions.has(data.loggedInAs.permissions, Permissions.USERADMIN.CREATE)}
<button type="submit" form="form_add_user" name="user" value="new">Add</button>
{/if}
</div>
<style>