mirror of
https://github.com/jakobkordez/ham-reserve.git
synced 2025-05-15 16:20:29 +00:00
Auth state rework
This commit is contained in:
parent
5b9bce4796
commit
dde9f5d4fb
@ -14,30 +14,30 @@
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.1",
|
||||
"@sveltejs/adapter-auto": "^3.3.1",
|
||||
"@sveltejs/kit": "^2.7.3",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||
"@sveltejs/kit": "^2.9.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.2",
|
||||
"@types/async-lock": "^1.4.2",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"async-lock": "^1.4.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.7",
|
||||
"daisyui": "^4.12.13",
|
||||
"eslint": "^9.13.0",
|
||||
"axios": "^1.7.9",
|
||||
"daisyui": "^4.12.20",
|
||||
"eslint": "^9.16.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.46.0",
|
||||
"globals": "^15.11.0",
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"globals": "^15.13.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-svelte": "^3.2.7",
|
||||
"svelte": "^5.1.2",
|
||||
"svelte-check": "^4.0.5",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-svelte": "^3.3.2",
|
||||
"svelte": "^5.10.0",
|
||||
"svelte-check": "^4.1.1",
|
||||
"svelte-fa": "^4.0.3",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"typescript": "^5.6.3",
|
||||
"typescript-eslint": "^8.11.0",
|
||||
"vite": "^5.4.10",
|
||||
"vitest": "^2.1.3"
|
||||
"tailwindcss": "^3.4.16",
|
||||
"typescript": "^5.7.2",
|
||||
"typescript-eslint": "^8.18.0",
|
||||
"vite": "^5.4.11",
|
||||
"vitest": "^2.1.8"
|
||||
}
|
||||
}
|
||||
|
1072
svelte-app/pnpm-lock.yaml
generated
1072
svelte-app/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,13 @@
|
||||
<script>
|
||||
import { Role } from '$lib/enums/role.enum';
|
||||
import { logout } from '$lib/stores/auth-store';
|
||||
import { userStore } from '$lib/stores/user-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import Fa from 'svelte-fa';
|
||||
import Clock from './clock.svelte';
|
||||
|
||||
let userDropOpen = $state(false);
|
||||
|
||||
const auth = getAuthContext();
|
||||
</script>
|
||||
|
||||
<div
|
||||
@ -20,11 +21,11 @@
|
||||
<!-- <ThemeToggle /> -->
|
||||
|
||||
<!-- User -->
|
||||
{#if $userStore}
|
||||
{#if auth.user}
|
||||
<div class="relative">
|
||||
<button class="btn btn-ghost" onclick={() => (userDropOpen = !userDropOpen)}>
|
||||
<Fa icon={faUserCircle} class="h-5 w-5" />
|
||||
<span>{$userStore.username}</span>
|
||||
<span>{auth.user.username}</span>
|
||||
</button>
|
||||
|
||||
<div class="absolute right-2 top-full z-[1] pt-1 {userDropOpen ? '' : 'hidden'}">
|
||||
@ -40,7 +41,7 @@
|
||||
<li>
|
||||
<a href="/profile" onclick={() => (userDropOpen = false)}>Profil</a>
|
||||
</li>
|
||||
{#if $userStore.roles.includes(Role.Admin)}
|
||||
{#if auth.user.roles.includes(Role.Admin)}
|
||||
<li>
|
||||
<a href="/admin" class="btn-warning" onclick={() => (userDropOpen = false)}>
|
||||
Admin panel
|
||||
@ -50,8 +51,8 @@
|
||||
<li>
|
||||
<button
|
||||
onclick={() => {
|
||||
logout();
|
||||
window.location.reload();
|
||||
auth.logout();
|
||||
userDropOpen = false;
|
||||
}}
|
||||
>
|
||||
Odjava
|
||||
|
@ -1,12 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { Fa } from 'svelte-fa';
|
||||
import { faWarning } from '@fortawesome/free-solid-svg-icons';
|
||||
import { userStore } from '$lib/stores/user-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
|
||||
const { class: className = '' }: { class?: string } = $props();
|
||||
|
||||
const auth = getAuthContext();
|
||||
</script>
|
||||
|
||||
{#if $userStore?.passwordResetRequired === true}
|
||||
{#if auth.user?.passwordResetRequired === true}
|
||||
<div class="alert alert-warning {className}">
|
||||
<Fa icon={faWarning} class="shrink-0 h-6 w-6" />
|
||||
<span>Opozorilo: Prosim, zamenjaj geslo!</span>
|
||||
|
111
svelte-app/src/lib/stores/auth-state.svelte.ts
Normal file
111
svelte-app/src/lib/stores/auth-state.svelte.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import AsyncLock from 'async-lock';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { getContext, setContext, tick } from 'svelte';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import { browser } from '$app/environment';
|
||||
import { type User } from '$lib/interfaces/user.interface';
|
||||
|
||||
type AuthState = {
|
||||
accessToken: string | null;
|
||||
refreshToken: string | null;
|
||||
};
|
||||
|
||||
function createAuthState() {
|
||||
const lock = new AsyncLock();
|
||||
let authState = $state<AuthState>(fetchStorage());
|
||||
let user = $state<User | null>();
|
||||
|
||||
$effect(() => {
|
||||
if (!browser) return;
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(authState));
|
||||
});
|
||||
|
||||
refreshUser();
|
||||
|
||||
function login(username: string, password: string) {
|
||||
return apiFunctions
|
||||
.login(username, password)
|
||||
.then((res) => {
|
||||
authState = res;
|
||||
tick().then(refreshUser);
|
||||
return true;
|
||||
})
|
||||
.catch(() => false);
|
||||
}
|
||||
|
||||
function refreshUser() {
|
||||
console.log('Refreshing user');
|
||||
return getAccessToken().then((token) => {
|
||||
if (!token) user = null;
|
||||
else apiFunctions.getMe(token).then((res) => (user = res));
|
||||
});
|
||||
}
|
||||
|
||||
async function getAccessToken(): Promise<string | null> {
|
||||
return await lock.acquire('isValid', async () => {
|
||||
const { accessToken } = authState;
|
||||
// Check access token validity
|
||||
if (accessToken) {
|
||||
const exp = jwtDecode(accessToken).exp ?? 0;
|
||||
if (Date.now() < exp * 1000) {
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
const { refreshToken } = fetchStorage();
|
||||
// Check refresh token validity
|
||||
if (refreshToken) {
|
||||
const exp = jwtDecode(refreshToken).exp ?? 0;
|
||||
if (Date.now() < exp * 1000) {
|
||||
// Try to refresh
|
||||
try {
|
||||
authState = await apiFunctions.refresh(refreshToken);
|
||||
console.log('Token refreshed');
|
||||
return authState.accessToken;
|
||||
} catch (e) {
|
||||
if (isAxiosError(e) && e.response?.status === 401) authState.refreshToken = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
get user() {
|
||||
return user;
|
||||
},
|
||||
login,
|
||||
getAccessToken,
|
||||
refreshUser,
|
||||
async logout(): Promise<void> {
|
||||
try {
|
||||
await apiFunctions.logout((await getAccessToken())!);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
authState = { accessToken: null, refreshToken: null };
|
||||
tick().then(refreshUser);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'auth-store';
|
||||
|
||||
function fetchStorage(): AuthState {
|
||||
if (!browser) return { accessToken: null, refreshToken: null };
|
||||
const value = localStorage.getItem(STORAGE_KEY) || '{}';
|
||||
return JSON.parse(value) as AuthState;
|
||||
}
|
||||
|
||||
const CONTEXT_KEY = 'AUTH_CONTEXT';
|
||||
|
||||
export function createAuthContext() {
|
||||
return setContext(CONTEXT_KEY, createAuthState());
|
||||
}
|
||||
|
||||
export function getAuthContext() {
|
||||
return getContext<ReturnType<typeof createAuthContext>>(CONTEXT_KEY);
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import AsyncLock from 'async-lock';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { get, writable } from 'svelte/store';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
const lock = new AsyncLock();
|
||||
|
||||
type AuthStore = {
|
||||
accessToken: string | null;
|
||||
refreshToken: string | null;
|
||||
};
|
||||
|
||||
const state: AuthStore = (() => {
|
||||
if (!browser) return { accessToken: null, refreshToken: null };
|
||||
const value = localStorage.getItem('auth-store') || '{}';
|
||||
return JSON.parse(value) as AuthStore;
|
||||
})();
|
||||
|
||||
export const authStore = writable<AuthStore>(state);
|
||||
|
||||
authStore.subscribe((value) => {
|
||||
if (!browser) return;
|
||||
localStorage.setItem('auth-store', JSON.stringify(value));
|
||||
});
|
||||
|
||||
export async function isAuthValid(): Promise<boolean> {
|
||||
return lock.acquire<boolean>('isValid', async () => {
|
||||
const { accessToken, refreshToken } = get(authStore);
|
||||
|
||||
// Check access token validity
|
||||
if (accessToken) {
|
||||
const exp = jwtDecode(accessToken).exp ?? 0;
|
||||
if (Date.now() < exp * 1000) {
|
||||
return true;
|
||||
} else authStore.update((v) => ({ ...v, accessToken: null }));
|
||||
}
|
||||
|
||||
// Check refresh token validity
|
||||
if (refreshToken) {
|
||||
const exp = jwtDecode(refreshToken).exp ?? 0;
|
||||
if (Date.now() < exp * 1000) {
|
||||
// Try to refresh
|
||||
try {
|
||||
authStore.set(await apiFunctions.refresh(refreshToken));
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (isAxiosError(e) && e.response?.status === 401)
|
||||
authStore.update((v) => ({ ...v, refreshToken: null }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAccessToken(): Promise<string | null> {
|
||||
const isValid = await isAuthValid();
|
||||
return isValid ? get(authStore).accessToken : null;
|
||||
}
|
||||
|
||||
export function logout(): void {
|
||||
try {
|
||||
apiFunctions.logout(get(authStore).accessToken!);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
authStore.set({ accessToken: null, refreshToken: null });
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import type { User } from '$lib/interfaces/user.interface';
|
||||
import { get, writable } from 'svelte/store';
|
||||
import { authStore, getAccessToken } from './auth-store';
|
||||
|
||||
export const userStore = writable<User | null>();
|
||||
|
||||
export const refreshUser = () =>
|
||||
getAccessToken()
|
||||
.then((token) => {
|
||||
if (!token) return null;
|
||||
return apiFunctions.getMe(token);
|
||||
})
|
||||
.then(userStore.set);
|
||||
|
||||
authStore.subscribe((auth) => {
|
||||
if (!auth.refreshToken) return userStore.set(null);
|
||||
if (!get(userStore)) refreshUser();
|
||||
});
|
@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import Header from '$lib/components/header.svelte';
|
||||
import { createAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import '../app.css';
|
||||
|
||||
createAuthContext();
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
|
@ -2,7 +2,9 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { Role } from '$lib/enums/role.enum';
|
||||
import { userStore } from '$lib/stores/user-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
const pathname = $derived($page.url.pathname);
|
||||
|
||||
@ -18,8 +20,8 @@
|
||||
];
|
||||
|
||||
$effect(() => {
|
||||
if ($userStore === undefined) return;
|
||||
if (!$userStore?.roles.includes(Role.Admin)) goto('/');
|
||||
if (auth.user === undefined) return;
|
||||
if (!auth.user?.roles.includes(Role.Admin)) goto('/');
|
||||
});
|
||||
|
||||
let { children } = $props();
|
||||
|
@ -2,11 +2,22 @@
|
||||
import { faAdd } from '@fortawesome/free-solid-svg-icons';
|
||||
import Fa from 'svelte-fa';
|
||||
import { getUTCString } from '$lib/util/date.util';
|
||||
import type { PageData } from './$types';
|
||||
import PrivateTag from '$lib/components/private-tag.svelte';
|
||||
import Loading from '$lib/components/loading.svelte';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import { apiFunctions } from '$lib/api';
|
||||
|
||||
export let data: PageData;
|
||||
const auth = getAuthContext();
|
||||
|
||||
const eventsP = auth
|
||||
.getAccessToken()
|
||||
.then((token) => {
|
||||
if (!token) return [];
|
||||
return apiFunctions.getAllEvents(token);
|
||||
})
|
||||
.then((events) => {
|
||||
return events.sort((a, b) => b.createdAt.valueOf() - a.createdAt.valueOf());
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
@ -19,7 +30,7 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{#await data.events}
|
||||
{#await eventsP}
|
||||
<Loading />
|
||||
{:then events}
|
||||
<div class="overflow-x-auto">
|
||||
|
@ -1,16 +0,0 @@
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = () => {
|
||||
return {
|
||||
events: getAccessToken()
|
||||
.then((token) => {
|
||||
if (!token) return [];
|
||||
return apiFunctions.getAllEvents(token);
|
||||
})
|
||||
.then((events) => {
|
||||
return events.sort((a, b) => b.createdAt.valueOf() - a.createdAt.valueOf());
|
||||
})
|
||||
};
|
||||
};
|
@ -1,23 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import Loading from '$lib/components/loading.svelte';
|
||||
import { uppercaseInput } from '$lib/input-helpers';
|
||||
import type { Event } from '$lib/interfaces/event.interface';
|
||||
import type { User } from '$lib/interfaces/user.interface';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import { faPlus, faRemove } from '@fortawesome/free-solid-svg-icons';
|
||||
import Fa from 'svelte-fa';
|
||||
|
||||
const { event }: { event: Event } = $props();
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
let users = $state<User[]>();
|
||||
let error = $state<string>();
|
||||
|
||||
let usernameInput = $state('');
|
||||
|
||||
$effect(() => {
|
||||
getAccessToken().then((token) => {
|
||||
auth.getAccessToken().then((token) => {
|
||||
if (!token) return;
|
||||
apiFunctions
|
||||
.getManyUsers(token, event.access)
|
||||
@ -29,11 +30,11 @@
|
||||
async function grantAccess() {
|
||||
error = undefined;
|
||||
try {
|
||||
const token = await getAccessToken();
|
||||
const token = await auth.getAccessToken();
|
||||
if (!token) throw Error('Unauthenticated');
|
||||
const user = await apiFunctions.findByUsername(token, usernameInput.toUpperCase());
|
||||
await apiFunctions.grantEventAccess(token, event._id, user._id);
|
||||
invalidateAll();
|
||||
users?.push(user);
|
||||
usernameInput = '';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -45,10 +46,10 @@
|
||||
|
||||
async function revokeAccess(userId: string) {
|
||||
try {
|
||||
const token = await getAccessToken();
|
||||
const token = await auth.getAccessToken();
|
||||
if (!token) throw Error('Unauthenticated');
|
||||
await apiFunctions.revokeEventAccess(token, event._id, userId);
|
||||
invalidateAll();
|
||||
users = users?.filter((u) => u._id !== userId);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
@ -4,13 +4,15 @@
|
||||
import type { Event } from '$lib/interfaces/event.interface';
|
||||
import type { Reservation } from '$lib/interfaces/reservation.interface';
|
||||
import type { User } from '$lib/interfaces/user.interface';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import { dayInMs, getUTCDateString, getUTCTimeString } from '$lib/util/date.util';
|
||||
import { faFileCircleCheck, faFileCircleExclamation } from '@fortawesome/free-solid-svg-icons';
|
||||
import Fa from 'svelte-fa';
|
||||
|
||||
const { event }: { event: Event } = $props();
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
let reservations = $state<Reservation[]>();
|
||||
let users = $state<Map<string, User>>();
|
||||
|
||||
@ -20,7 +22,7 @@
|
||||
.then((res) => {
|
||||
reservations = res.sort((a, b) => b.startDateTime.valueOf() - a.startDateTime.valueOf());
|
||||
const u = new Set<string>(res.map((r) => r.user));
|
||||
getAccessToken().then((token) => {
|
||||
auth.getAccessToken().then((token) => {
|
||||
if (!token) return;
|
||||
apiFunctions
|
||||
.getManyUsers(token, Array.from(u))
|
||||
|
@ -4,17 +4,22 @@
|
||||
import Loading from '$lib/components/loading.svelte';
|
||||
import { Role } from '$lib/enums/role.enum';
|
||||
import type { User } from '$lib/interfaces/user.interface';
|
||||
import type { PageData } from './$types';
|
||||
import { userStore } from '$lib/stores/user-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import DeleteModal from './delete-modal.svelte';
|
||||
import ResetPasswordModal from './reset-password-modal.svelte';
|
||||
import { apiFunctions } from '$lib/api';
|
||||
|
||||
const { data }: { data: PageData } = $props();
|
||||
const auth = getAuthContext();
|
||||
|
||||
let deleteUser = $state<User>();
|
||||
let resetPassUser = $state<User>();
|
||||
|
||||
let me = $derived($userStore);
|
||||
let me = $derived(auth.user);
|
||||
|
||||
const usersP = auth.getAccessToken().then((token) => {
|
||||
if (!token) return [];
|
||||
return apiFunctions.getAllUsers(token);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
@ -27,7 +32,7 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{#await data.users}
|
||||
{#await usersP}
|
||||
<Loading />
|
||||
{:then users}
|
||||
<div class="overflow-x-auto">
|
||||
|
@ -1,12 +0,0 @@
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = () => {
|
||||
return {
|
||||
users: getAccessToken().then((token) => {
|
||||
if (!token) return [];
|
||||
return apiFunctions.getAllUsers(token);
|
||||
})
|
||||
};
|
||||
};
|
@ -2,11 +2,13 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import { uppercaseInput } from '$lib/input-helpers';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
|
||||
import Fa from 'svelte-fa';
|
||||
import PasswordField from '../password-field.svelte';
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
let username = $state('');
|
||||
let password = $state('');
|
||||
let name = $state('');
|
||||
@ -16,7 +18,7 @@
|
||||
|
||||
function submit() {
|
||||
error = undefined;
|
||||
getAccessToken().then((token) => {
|
||||
auth.getAccessToken().then((token) => {
|
||||
if (!token) return;
|
||||
apiFunctions
|
||||
.createUser(token, {
|
||||
|
@ -2,9 +2,11 @@
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import type { User } from '$lib/interfaces/user.interface';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
|
||||
let { deleteUser = $bindable() }: { deleteUser: User | undefined } = $props();
|
||||
|
||||
const auth = getAuthContext();
|
||||
</script>
|
||||
|
||||
<dialog class="modal {deleteUser ? 'modal-open' : ''}">
|
||||
@ -19,7 +21,7 @@
|
||||
class="btn btn-error"
|
||||
onclick={async () => {
|
||||
deleteUser = undefined;
|
||||
const token = await getAccessToken();
|
||||
const token = await auth.getAccessToken();
|
||||
if (!token) return;
|
||||
apiFunctions
|
||||
.deleteUser(token, deleteUser!._id)
|
||||
|
@ -1,13 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import type { User } from '$lib/interfaces/user.interface';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { tick } from 'svelte';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import PasswordField from './password-field.svelte';
|
||||
|
||||
let { user = $bindable() }: { user: User | undefined } = $props();
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
let password = $state('');
|
||||
</script>
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
onclick={async () => {
|
||||
const token = await getAccessToken();
|
||||
const token = await auth.getAccessToken();
|
||||
if (!token) return;
|
||||
apiFunctions
|
||||
.updateOtherPassword(token, user!._id, password)
|
||||
|
@ -6,13 +6,15 @@
|
||||
import Fa from 'svelte-fa';
|
||||
import FreeDatesComponent from './free-dates-component.svelte';
|
||||
import Time from './time.svelte';
|
||||
import { userStore } from '$lib/stores/user-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import ReserveComponent from './reserve/reserve-component.svelte';
|
||||
import MyReservations from './reserve/my-reservations.svelte';
|
||||
import { Role } from '$lib/enums/role.enum';
|
||||
import PrivateTag from '$lib/components/private-tag.svelte';
|
||||
|
||||
const { event }: { event: Event } = $props();
|
||||
|
||||
const auth = getAuthContext();
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-8">
|
||||
@ -28,7 +30,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if $userStore?.roles.includes(Role.Admin)}
|
||||
{#if auth.user?.roles.includes(Role.Admin)}
|
||||
<a href="/admin/events/{event._id}" class="btn btn-warning btn-sm">Uredi</a>
|
||||
{/if}
|
||||
</div>
|
||||
@ -49,7 +51,7 @@
|
||||
<FreeDatesComponent {event} />
|
||||
</div>
|
||||
|
||||
{#if $userStore && event.access.includes($userStore._id)}
|
||||
{#if auth.user && event.access.includes(auth.user._id)}
|
||||
<div>
|
||||
<h2 class="mb-4 text-2xl">Moje rezervacije</h2>
|
||||
<MyReservations eventId={event._id} />
|
||||
@ -63,7 +65,7 @@
|
||||
</div>
|
||||
<ReserveComponent {event} />
|
||||
</div>
|
||||
{:else if !$userStore}
|
||||
{:else if !auth.user}
|
||||
<div>
|
||||
<a href="/login?redirect=/event/{event._id}" class="btn">Prijavi se za rezervacijo</a>
|
||||
</div>
|
||||
|
@ -2,11 +2,13 @@
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import Loading from '$lib/components/loading.svelte';
|
||||
import ReservationsTable from '$lib/components/reservations-table.svelte';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
|
||||
const { eventId }: { eventId: string } = $props();
|
||||
|
||||
const reservations = getAccessToken().then((token) => {
|
||||
const auth = getAuthContext();
|
||||
|
||||
const reservations = auth.getAccessToken().then((token) => {
|
||||
if (!token) return [];
|
||||
return apiFunctions
|
||||
.getReservationsForSelf(token, { event: eventId })
|
||||
|
@ -10,9 +10,11 @@
|
||||
import BandSelector from './band-selector.svelte';
|
||||
import ModeSelector from './mode-selector.svelte';
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import { clampDate } from '..';
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
function floorHour(date: number) {
|
||||
return new Date(Math.floor(date / hourInMs) * hourInMs);
|
||||
}
|
||||
@ -92,7 +94,7 @@
|
||||
|
||||
async function submit() {
|
||||
error = undefined;
|
||||
const token = await getAccessToken();
|
||||
const token = await auth.getAccessToken();
|
||||
if (!token) {
|
||||
error = ['Napaka'];
|
||||
return;
|
||||
|
@ -1,16 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import { uppercaseInput } from '$lib/input-helpers';
|
||||
import { authStore, isAuthValid } from '$lib/stores/auth-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
|
||||
const redirect = $page.url.searchParams.get('redirect');
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
$effect(() => {
|
||||
isAuthValid().then((r) => {
|
||||
if (r) goto(redirect || '/');
|
||||
});
|
||||
if (auth.user) goto(redirect || '/');
|
||||
});
|
||||
|
||||
let username = $state('');
|
||||
@ -21,16 +20,9 @@
|
||||
function login() {
|
||||
error = undefined;
|
||||
|
||||
apiFunctions
|
||||
.login(username, password)
|
||||
.then((res) => {
|
||||
authStore.set(res);
|
||||
goto(redirect || '/');
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
error = 'Napačno uporabniško ime ali geslo';
|
||||
});
|
||||
auth.login(username, password).then((res) => {
|
||||
if (!res) error = 'Napačno uporabniško ime ali geslo';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,14 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import Loading from '$lib/components/loading.svelte';
|
||||
import ReservationsTable from '$lib/components/reservations-table.svelte';
|
||||
import ResetPasswordAlert from '$lib/components/reset-password-alert.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
|
||||
const { data }: { data: PageData } = $props();
|
||||
const auth = getAuthContext();
|
||||
|
||||
const promise = auth.getAccessToken().then((token) => {
|
||||
if (!token) throw Error('Unauthenticated');
|
||||
return Promise.all([
|
||||
apiFunctions.getMe(token),
|
||||
apiFunctions.getReservationsForSelf(token).then((res) => {
|
||||
return res.sort((a, b) => b.startDateTime.valueOf() - a.startDateTime.valueOf());
|
||||
})
|
||||
]);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="container flex flex-col gap-8 py-10">
|
||||
{#await Promise.all([data.user, data.reservations])}
|
||||
{#await promise}
|
||||
<Loading />
|
||||
{:then [user, reservations]}
|
||||
<ResetPasswordAlert />
|
||||
|
@ -1,17 +0,0 @@
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async () => {
|
||||
const tokenP = getAccessToken().then((token) => {
|
||||
if (!token) throw Error('Unauthenticated');
|
||||
return token;
|
||||
});
|
||||
|
||||
return {
|
||||
user: tokenP.then(apiFunctions.getMe),
|
||||
reservations: tokenP.then(apiFunctions.getReservationsForSelf).then((res) => {
|
||||
return res.sort((a, b) => b.startDateTime.valueOf() - a.startDateTime.valueOf());
|
||||
})
|
||||
};
|
||||
};
|
@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { refreshUser, userStore } from '$lib/stores/user-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import { faCircleCheck, faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
|
||||
import Fa from 'svelte-fa';
|
||||
|
||||
const user = $derived($userStore);
|
||||
const auth = getAuthContext();
|
||||
const user = $derived(auth.user);
|
||||
|
||||
let name = $state('');
|
||||
let email = $state('');
|
||||
@ -28,7 +28,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const token = await getAccessToken();
|
||||
const token = await auth.getAccessToken();
|
||||
if (!token) return;
|
||||
apiFunctions
|
||||
.updateSelf(token, {
|
||||
@ -38,7 +38,7 @@
|
||||
})
|
||||
.then(() => {
|
||||
saved = true;
|
||||
refreshUser();
|
||||
auth.refreshUser();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
@ -55,7 +55,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const token = await getAccessToken();
|
||||
const token = await auth.getAccessToken();
|
||||
if (!token) return;
|
||||
apiFunctions
|
||||
.updateSelfPassword(token, oldPassword, newPassword)
|
||||
|
@ -11,9 +11,8 @@
|
||||
import DownloadButton from './download-button.svelte';
|
||||
import LogSummary from './log-summary.svelte';
|
||||
import UploadLog from './upload-log.svelte';
|
||||
import { userStore } from '$lib/stores/user-store';
|
||||
import { Role } from '$lib/enums/role.enum';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import ProgressBar from '$lib/components/progress-bar.svelte';
|
||||
import { createTimeState } from '$lib/stores/time-state.svelte';
|
||||
@ -21,11 +20,19 @@
|
||||
|
||||
const { data }: { data: PageData } = $props();
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
const promise = auth.getAccessToken().then((token) => {
|
||||
if (!token) throw new Error('Unauthenticated');
|
||||
const res = apiFunctions.getReservation(token, data.id);
|
||||
return Promise.all([res, res.then((r) => apiFunctions.getEvent(r.event))]);
|
||||
});
|
||||
|
||||
const now = createTimeState(10_000);
|
||||
</script>
|
||||
|
||||
<div class="container py-10">
|
||||
{#await Promise.all([data.reservation, data.event])}
|
||||
{#await promise}
|
||||
<Loading />
|
||||
{:then [reservation, event]}
|
||||
<div class="flex flex-col gap-10">
|
||||
@ -36,7 +43,7 @@
|
||||
<span>Nazaj na dogodek</span>
|
||||
</a>
|
||||
|
||||
{#if $userStore?.roles.includes(Role.Admin)}
|
||||
{#if auth.user?.roles.includes(Role.Admin)}
|
||||
<a href="/admin/events/{event._id}" class="btn btn-warning btn-sm btn-outline">
|
||||
Nazaj na dogodek
|
||||
</a>
|
||||
@ -44,7 +51,7 @@
|
||||
<button
|
||||
onclick={() => {
|
||||
if (!confirm('Ali ste prepričani, da želite izbrisati rezervacijo?')) return;
|
||||
getAccessToken().then((token) => {
|
||||
auth.getAccessToken().then((token) => {
|
||||
if (!token) return;
|
||||
apiFunctions.deleteReservation(token, reservation._id).then(() => {
|
||||
window.location.href = `/event/${event._id}`;
|
||||
|
@ -1,17 +1,7 @@
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = ({ params }) => {
|
||||
const tokenP = getAccessToken();
|
||||
|
||||
const res = tokenP.then((token) => {
|
||||
if (!token) throw Error('Unauthenticated');
|
||||
return apiFunctions.getReservation(token, params.id);
|
||||
});
|
||||
|
||||
return {
|
||||
reservation: res,
|
||||
event: res.then((res) => apiFunctions.getEvent(res.event))
|
||||
id: params.id
|
||||
};
|
||||
};
|
||||
|
@ -2,13 +2,15 @@
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import type { Event } from '$lib/interfaces/event.interface';
|
||||
import type { Reservation } from '$lib/interfaces/reservation.interface';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
import { faDownload } from '@fortawesome/free-solid-svg-icons';
|
||||
import { tick } from 'svelte';
|
||||
import Fa from 'svelte-fa';
|
||||
|
||||
const { event, reservation }: { event: Event; reservation: Reservation } = $props();
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
let aRef: HTMLAnchorElement;
|
||||
let url = $state<string>();
|
||||
|
||||
@ -24,7 +26,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const token = await getAccessToken();
|
||||
const token = await auth.getAccessToken();
|
||||
if (!token) return;
|
||||
apiFunctions
|
||||
.getLog(token, reservation._id)
|
||||
|
@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { apiFunctions } from '$lib/api';
|
||||
import type { Reservation } from '$lib/interfaces/reservation.interface';
|
||||
import { getAccessToken } from '$lib/stores/auth-store';
|
||||
import { userStore } from '$lib/stores/user-store';
|
||||
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||
|
||||
const { reservation: res }: { reservation: Reservation } = $props();
|
||||
|
||||
const auth = getAuthContext();
|
||||
|
||||
let error = $state<string>();
|
||||
let file = $state<File>();
|
||||
|
||||
@ -16,7 +17,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const token = await getAccessToken();
|
||||
const token = await auth.getAccessToken();
|
||||
if (!token) return;
|
||||
apiFunctions
|
||||
.uploadLog(token, res._id, file)
|
||||
@ -30,7 +31,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $userStore?._id === res.user}
|
||||
{#if auth.user?._id === res.user}
|
||||
<div class="form-control">
|
||||
<div class="label">
|
||||
{res.logSummary ? 'Ponovno oddaj' : 'Oddaj'} dnevnik
|
||||
|
Loading…
x
Reference in New Issue
Block a user