Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/lib/components/Dialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
create?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
children: any;
canOverflow: boolean;
requireCloseConfirmation: boolean;
canOverflow?: boolean;
requireCloseConfirmation?: boolean;
} = $props();

let isConfirmationOpen = $state(false);
Expand Down
14 changes: 4 additions & 10 deletions src/lib/components/ItemCardOptions.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts">
import type { IBasicItemPopulated } from "$lib/server/db/models/basicItem.js";
import { permissionsAllowEdit } from "$lib/stores/loginStore.js";
import { CopyIcon, SquarePenIcon } from "@lucide/svelte";
import { createEventDispatcher } from "svelte";
import { getEditOnLogin, login } from "../stores/loginStore.js";
import CreateItem from "./CreateItem.svelte";
import Duplicate from "./Duplicate.svelte";

Expand All @@ -19,20 +19,14 @@
let unique = $state({});

function duplicateFunction(item: IBasicItemPopulated) {
if (
!getEditOnLogin() ||
($login?.isLoggedIn && $login?.permissionLevel > 1)
) {
if (permissionsAllowEdit(2)) {
duplicator?.changeItem(item);
duplicateDialog?.showModal();
}
}

function duplicateEditFunction(item: IBasicItemPopulated) {
if (
!getEditOnLogin() ||
($login?.isLoggedIn && $login?.permissionLevel > 1)
) {
if (permissionsAllowEdit(2)) {
creator?.changeItem(item);
createDialog?.showModal();
}
Expand All @@ -48,7 +42,7 @@
</script>

<div class="simple-flex">
{#if !getEditOnLogin() || ($login?.isLoggedIn && $login?.permissionLevel > 1)}
{#if permissionsAllowEdit(2)}
<!--Duplicate button-->
<button
class="border-button hoverable"
Expand Down
8 changes: 4 additions & 4 deletions src/lib/components/ItemDetails.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import type { IBasicItemPopulated } from "$lib/server/db/models/basicItem.js";
import { getItemNameGivenId } from "$lib/stores/createItemStore.svelte";
import { getEditOnLogin, login } from "$lib/stores/loginStore.js";
import { currentPermissionLevelIsAtLeast, permissionsAllowEdit } from "$lib/stores/loginStore.js";
import {
FolderTreeIcon,
HouseIcon,
Expand Down Expand Up @@ -212,7 +212,7 @@
</h1>

<div class="button-row-flex">
{#if !getEditOnLogin() || ($login?.isLoggedIn && $login?.permissionLevel > 0)}
{#if permissionsAllowEdit(1)}
<button
title="Move"
class="border-button center-button-icons flex-grow font-semibold shadow"
Expand All @@ -227,7 +227,7 @@
<HouseIcon class="icon-small" />
</button>

{#if !getEditOnLogin() || ($login?.isLoggedIn && $login?.permissionLevel > 1)}
{#if permissionsAllowEdit(2)}
<button
title="Edit"
class="border-button center-button-icons flex-grow font-semibold shadow"
Expand All @@ -245,7 +245,7 @@
</button>
{/if}

{#if ($login?.permissionLevel ?? 1) > 2}
{#if currentPermissionLevelIsAtLeast(3)}
<button
title="Delete"
class="warn-button center-button-icons flex-grow font-semibold shadow"
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/Menu.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { browser } from "$app/environment";
import OAuth from "$lib/components/OAuth.svelte";
import { login } from "$lib/stores/loginStore.js";
import { currentPermissionLevel } from "$lib/stores/loginStore.js";
import "$lib/styles/main.css";

let {
Expand Down Expand Up @@ -35,7 +35,7 @@
}
});

const permissionLevel = $derived($login?.permissionLevel ?? 0);
const permissionLevel = $derived(currentPermissionLevel());
</script>

{#if open}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/TemplateList.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import type { ITemplatePopulated } from "$lib/server/db/models/template.js";
import { getEditOnLogin, login } from "../stores/loginStore.js";
import { permissionsAllowEdit } from "$lib/stores/loginStore.js";
import DeleteTemplate from "./DeleteTemplate.svelte";
import Dialog from "./Dialog.svelte";
import EditTemplate from "./EditTemplate.svelte";
Expand Down Expand Up @@ -50,7 +50,7 @@
</ul>
</div>

{#if !getEditOnLogin() || ($login?.isLoggedIn && $login?.permissionLevel > 2)}
{#if permissionsAllowEdit(3)}
<DeleteTemplate
templateId={template._id.toString()}
onDelete={handleDelete}>
Expand Down
7 changes: 3 additions & 4 deletions src/lib/components/UserList.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { getEditOnLogin, login, toggleEditOnLogin } from '$lib/stores/loginStore.js';
import { getCanOnlyEditWhenLoggedIn, login, setCanOnlyEditWhenLoggedIn } from '$lib/stores/loginStore.js';

interface User {
id: string;
Expand Down Expand Up @@ -125,8 +125,7 @@
}

function toggleUserEditOnLogin() {
const current = getEditOnLogin();
toggleEditOnLogin(!current);
setCanOnlyEditWhenLoggedIn(!getCanOnlyEditWhenLoggedIn());
}
</script>

Expand All @@ -144,7 +143,7 @@
type="checkbox"
id="toggle-slider"
class="sr-only peer"
checked={getEditOnLogin()}
checked={getCanOnlyEditWhenLoggedIn()}
onchange={toggleUserEditOnLogin}
>
<div class="w-11 h-6 bg-gray-700 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-600 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
Expand Down
7 changes: 3 additions & 4 deletions src/lib/server/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import jwt from 'jsonwebtoken';
import type { RequestEvent } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';
import jwt from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET || 'your_jwt_secret_key';

Expand All @@ -19,11 +19,10 @@ interface OAuthJWTPayload {

type JWTPayload = UserJWTPayload | OAuthJWTPayload;

function isOAuthPayload(payload: JWTPayload): payload is OAuthJWTPayload {
export function isOAuthPayload(payload: JWTPayload): payload is OAuthJWTPayload {
return 'sub_id' in payload;
}


export function verifyToken(token: string | undefined): JWTPayload | null {
if (!token || token === '') return null;

Expand Down Expand Up @@ -75,4 +74,4 @@ export function getUserId(payload: JWTPayload): string {

export function getUserName(payload: JWTPayload): string {
return isOAuthPayload(payload) ? payload.name : payload.username;
}
}
4 changes: 2 additions & 2 deletions src/lib/server/db/models/authenticatorAccount.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import mongoose from 'mongoose';
import type { Document } from 'mongoose';
import mongoose from 'mongoose';
const { Schema, model } = mongoose;

export interface IAuthenticatorAccount extends Document {
Expand All @@ -22,4 +22,4 @@ interface AuthenticatorAccountModel extends mongoose.Model<IAuthenticatorAccount
findByUsername(username: string): Promise<IAuthenticatorAccount | null>;
}

export const AuthenticatorAccount = model<IAuthenticatorAccount, AuthenticatorAccountModel>('AuthenticatorAccount', AuthenticatorAccountSchema);
export const AuthenticatorAccount = model<IAuthenticatorAccount, AuthenticatorAccountModel>('AuthenticatorAccount', AuthenticatorAccountSchema);
4 changes: 2 additions & 2 deletions src/lib/server/db/models/login.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import mongoose, { Types } from 'mongoose';
import type { Document } from 'mongoose';
import mongoose, { Types } from 'mongoose';
const { Schema, model } = mongoose;

export enum ServiceType {
Expand Down Expand Up @@ -37,4 +37,4 @@ interface LoginModel extends mongoose.Model<ILogin> {
findByLoginId(login_id: string): Promise<ILogin | null>;
}

export const Login = model<ILogin, LoginModel>('Login', LoginSchema);
export const Login = model<ILogin, LoginModel>('Login', LoginSchema);
4 changes: 2 additions & 2 deletions src/lib/server/db/models/user.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import mongoose, { Types } from 'mongoose';
import bcrypt from 'bcryptjs';
import type { Document } from 'mongoose';
import mongoose, { Types } from 'mongoose';
const { Schema, model } = mongoose;
import bcrypt from 'bcryptjs';

export interface IUser extends Document {
_id: Types.ObjectId;
Expand Down
34 changes: 25 additions & 9 deletions src/lib/stores/loginStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { writable } from 'svelte/store';
import { get, writable } from 'svelte/store';

export interface LoginState {
isLoggedIn: boolean;
Expand All @@ -7,25 +7,41 @@ export interface LoginState {
permissionLevel: number;
}


const initialState: LoginState = {
isLoggedIn: false,
name: '',
sub_id: '',
permissionLevel: 0,
};

export function toggleEditOnLogin(value?: boolean) {
if (value !== undefined && typeof window !== 'undefined') {
localStorage.setItem('editOnLogin', String(value));
export const login = writable<LoginState>(initialState);

export function getCanOnlyEditWhenLoggedIn() {
if (typeof window === 'undefined') {
throw new Error('Could not get permission canOnlyEditWhenLoggedIn: window is undefined');
}

return localStorage.getItem('editOnLogin') === 'true';
}

export function getEditOnLogin() {
export function setCanOnlyEditWhenLoggedIn(value: boolean) {
if (typeof window === 'undefined') {
return false;
throw new Error('Could not set permission canOnlyEditWhenLoggedIn: window is undefined');
}
return localStorage.getItem('editOnLogin') === 'true';

localStorage.setItem('editOnLogin', String(value));
}

export function permissionsAllowEdit(minimumPermissionLevel: number) {
return !getCanOnlyEditWhenLoggedIn() || currentPermissionLevelIsAtLeast(minimumPermissionLevel);
}

export const login = writable<LoginState>(initialState);
export function currentPermissionLevel() {
const currentLogin = get(login);
return currentLogin.permissionLevel ?? 0;
}

export function currentPermissionLevelIsAtLeast(level: number) {
const currentLogin = get(login);
return currentLogin?.isLoggedIn && currentLogin?.permissionLevel >= level;
}
24 changes: 24 additions & 0 deletions src/lib/utility/loginHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { json } from '@sveltejs/kit';
import jwt from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET || 'your_jwt_secret_key';
const TOKEN_EXPIRATION = '24h';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function loginCore(user: any, successMessage: string, successCode: number){
const token = jwt.sign(
{ id: user._id, username: user.username, permissionLevel: user.permissionLevel },
JWT_SECRET,
{ expiresIn: TOKEN_EXPIRATION }
);

return json({
message: successMessage,
token,
user: {
id: user._id,
username: user.username,
permissionLevel: user.permissionLevel
}
}, { status: successCode });
}
10 changes: 2 additions & 8 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
import Window from "$lib/components/Window.svelte";
import type { IBasicItemPopulated } from "$lib/server/db/models/basicItem.js";
import { dragDropMode, setDragDropMode } from "$lib/stores/dragDropStore.js";
import type { LoginState } from "$lib/stores/loginStore.js";
import { getEditOnLogin, login } from "$lib/stores/loginStore.js";
import { permissionsAllowEdit } from "$lib/stores/loginStore.js";
import { topBarHeight } from "$lib/stores/topBarStore.js";
import "$lib/styles/main.css";
import type { ItemWindow } from "$lib/utility/pageHelper.js";
Expand Down Expand Up @@ -59,11 +58,6 @@

let itemTreeRef = $state<{ reload: () => Promise<void> } | null>(null);

let currentLogin = $state<LoginState | undefined>();
login.subscribe((value) => {
currentLogin = value;
});

$effect(() => {
if (showMoveDialog) {
moveDialog?.showModal();
Expand Down Expand Up @@ -354,7 +348,7 @@
{/each}
{/if}

{#if !getEditOnLogin() || (currentLogin?.isLoggedIn && currentLogin?.permissionLevel > 1)}
{#if permissionsAllowEdit(2)}
<button
class="add-button text-icon font-bold shadow"
onclick={() => {
Expand Down
27 changes: 5 additions & 22 deletions src/routes/api/auth/login/+server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from '@sveltejs/kit';
import jwt from 'jsonwebtoken';
import User from '$lib/server/db/models/user.js';

const JWT_SECRET = process.env.JWT_SECRET || 'your_jwt_secret_key';
import { loginCore } from '$lib/utility/loginHelper';
import type { RequestHandler } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';

export const POST: RequestHandler = async ({ request }) => {
const { username, password } = await request.json();
Expand All @@ -20,20 +18,5 @@ export const POST: RequestHandler = async ({ request }) => {
throw error(401, 'Invalid credentials');
}

// Generate JWT token
const token = jwt.sign(
{ id: user._id, username: user.username, permissionLevel: user.permissionLevel },
JWT_SECRET,
{ expiresIn: '24h' }
);

return json({
message: 'Login successful',
token,
user: {
id: user._id,
username: user.username,
permissionLevel: user.permissionLevel
}
}, { status: 200 });
};
return loginCore(user, 'Login successful', 200);
};
7 changes: 4 additions & 3 deletions src/routes/api/auth/permissions/+server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { requirePermissionLevel } from '$lib/server/auth.js';
import { isOAuthPayload, requirePermissionLevel } from '$lib/server/auth.js';
import User from '$lib/server/db/models/user.js';
import type { RequestHandler } from '@sveltejs/kit';
import { error, json } from '@sveltejs/kit';
Expand Down Expand Up @@ -28,7 +28,8 @@ export const PUT: RequestHandler = async (event) => {
}

// Don't allow modifying own permissions
if (targetUser._id.toString() === currentUser.id) {
let currentUserId: string = isOAuthPayload(currentUser) ? currentUser.sub_id : currentUser.id;
if (targetUser._id.toString() === currentUserId) {
throw error(400, 'Cannot modify your own permission level');
}

Expand All @@ -53,4 +54,4 @@ export const PUT: RequestHandler = async (event) => {
permissionLevel: targetUser.permissionLevel
}
});
};
};
Loading
Loading