mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
feat(web): improve /auth pages (#1969)
* feat(web): improve /auth pages * invalidate load functions after login * handle login server errors more graceful * add loading state to oauth button
This commit is contained in:
parent
04955a4123
commit
87d84b922f
@ -1,14 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { api } from '@api';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
|
||||
let password = '';
|
||||
let confirmPassowrd = '';
|
||||
|
||||
let canRegister = false;
|
||||
|
||||
$: {
|
||||
@ -21,13 +18,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function registerAdmin(event: SubmitEvent) {
|
||||
async function registerAdmin(event: SubmitEvent & { currentTarget: HTMLFormElement }) {
|
||||
if (canRegister) {
|
||||
error = '';
|
||||
|
||||
const formElement = event.target as HTMLFormElement;
|
||||
|
||||
const form = new FormData(formElement);
|
||||
const form = new FormData(event.currentTarget);
|
||||
|
||||
const email = form.get('email');
|
||||
const password = form.get('password');
|
||||
@ -42,7 +37,7 @@
|
||||
});
|
||||
|
||||
if (status === 201) {
|
||||
goto('/auth/login');
|
||||
goto(AppRoute.AUTH_LOGIN);
|
||||
return;
|
||||
} else {
|
||||
error = 'Error create admin account';
|
||||
@ -52,81 +47,74 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
|
||||
>
|
||||
<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
|
||||
<ImmichLogo class="text-center" height="100" width="100" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
Admin Registration
|
||||
</h1>
|
||||
<p
|
||||
class="text-sm border rounded-md p-4 font-mono text-gray-600 dark:border-immich-dark-bg dark:text-gray-300"
|
||||
>
|
||||
Since you are the first user on the system, you will be assigned as the Admin and are
|
||||
responsible for administrative tasks, and additional users will be created by you.
|
||||
</p>
|
||||
<form on:submit|preventDefault={registerAdmin} method="post" class="flex flex-col gap-5 mt-5">
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Admin Email</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<form on:submit|preventDefault={registerAdmin} method="post" action="" autocomplete="off">
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Admin Email</label>
|
||||
<input class="immich-form-input" id="email" name="email" type="email" required />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="password">Admin Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
required
|
||||
bind:value={password}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="password">Admin Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
required
|
||||
bind:value={password}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="confirmPassword">Confirm Admin Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="confirmPassword"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
required
|
||||
bind:value={confirmPassowrd}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="confirmPassword">Confirm Admin Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="confirmPassword"
|
||||
name="password"
|
||||
type="password"
|
||||
required
|
||||
bind:value={confirmPassowrd}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="firstName">First Name</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="firstName"
|
||||
name="firstName"
|
||||
type="text"
|
||||
autocomplete="given-name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="firstName">First Name</label>
|
||||
<input class="immich-form-input" id="firstName" name="firstName" type="text" required />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="lastName">Last Name</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="lastName"
|
||||
name="lastName"
|
||||
type="text"
|
||||
autocomplete="family-name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="lastName">Last Name</label>
|
||||
<input class="immich-form-input" id="lastName" name="lastName" type="text" required />
|
||||
</div>
|
||||
{#if error}
|
||||
<p class="text-red-400">{error}</p>
|
||||
{/if}
|
||||
|
||||
{#if error}
|
||||
<p class="text-red-400 ml-4">{error}</p>
|
||||
{/if}
|
||||
|
||||
{#if success}
|
||||
<div>
|
||||
<p>Admin account has been registered</p>
|
||||
<p>
|
||||
<a href="/auth/login">Login</a>
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex w-full">
|
||||
<button
|
||||
type="submit"
|
||||
class="m-4 p-2 bg-immich-primary dark:bg-immich-dark-primary hover:bg-immich-primary/75 dark:hover:bg-immich-dark-primary/80 dark:text-immich-dark-gray px-6 py-4 text-white rounded-md shadow-md w-full"
|
||||
>Sign Up</button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="my-5 flex w-full">
|
||||
<button type="submit" class="immich-btn-primary-big">Sign Up</button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { api, UserResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
|
||||
export let user: UserResponseDto;
|
||||
let error: string;
|
||||
@ -44,61 +43,41 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="border bg-gray-50 dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
|
||||
>
|
||||
<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
|
||||
<ImmichLogo class="text-center" height="100" width="100" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
Change Password
|
||||
</h1>
|
||||
|
||||
<p
|
||||
class="text-sm border rounded-3xl p-6 text-gray-600 dark:border-immich-dark-bg dark:text-gray-300 bg-immich-bg dark:bg-gray-900"
|
||||
>
|
||||
Hi {user.firstName}
|
||||
{user.lastName} ({user.email}),
|
||||
<br />
|
||||
<br />
|
||||
This is either the first time you are signing into the system or a request has been made to change
|
||||
your password. Please enter the new password below.
|
||||
</p>
|
||||
<form on:submit|preventDefault={changePassword} method="post" class="flex flex-col gap-5 mt-5">
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="password">New Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
required
|
||||
bind:value={password}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<form on:submit|preventDefault={changePassword} method="post" autocomplete="off">
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="password">New Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
required
|
||||
bind:value={password}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="confirmPassword">Confirm Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="confirmPassword"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
bind:value={confirmPassowrd}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="confirmPassword">Confirm Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="confirmPassword"
|
||||
name="password"
|
||||
type="password"
|
||||
required
|
||||
bind:value={confirmPassowrd}
|
||||
/>
|
||||
</div>
|
||||
{#if error}
|
||||
<p class="text-red-400 text-sm">{error}</p>
|
||||
{/if}
|
||||
|
||||
{#if error}
|
||||
<p class="text-red-400 ml-4 text-sm">{error}</p>
|
||||
{/if}
|
||||
|
||||
{#if success}
|
||||
<p class="text-immich-primary ml-4 text-sm">{success}</p>
|
||||
{/if}
|
||||
<div class="flex w-full">
|
||||
<button type="submit" class="immich-btn-primary-big m-4">Change Password</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{#if success}
|
||||
<p class="text-immich-primary text-sm">{success}</p>
|
||||
{/if}
|
||||
<div class="my-5 flex w-full">
|
||||
<button type="submit" class="immich-btn-primary-big">Change Password</button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,33 +1,33 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { loginPageMessage } from '$lib/constants';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { api, oauth, OAuthConfigResponseDto } from '@api';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
|
||||
let error: string;
|
||||
let email = '';
|
||||
let password = '';
|
||||
let oauthError: string;
|
||||
let authConfig: OAuthConfigResponseDto = { enabled: false, passwordLoginEnabled: false };
|
||||
let loading = true;
|
||||
export let authConfig: OAuthConfigResponseDto;
|
||||
let loading = false;
|
||||
let oauthLoading = true;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
onMount(async () => {
|
||||
if (oauth.isCallback(window.location)) {
|
||||
try {
|
||||
loading = true;
|
||||
await oauth.login(window.location);
|
||||
dispatch('success');
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error('Error [login-form] [oauth.callback]', e);
|
||||
oauthError = 'Unable to complete OAuth login';
|
||||
loading = false;
|
||||
} finally {
|
||||
oauthLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
const { enabled, url, autoLaunch } = authConfig;
|
||||
|
||||
if (enabled && url && autoLaunch && !oauth.isAutoLaunchDisabled(window.location)) {
|
||||
await goto('/auth/login?autoLaunch=0', { replaceState: true });
|
||||
await goto(`${AppRoute.AUTH_LOGIN}?autoLaunch=0`, { replaceState: true });
|
||||
await goto(url);
|
||||
return;
|
||||
}
|
||||
@ -47,7 +47,7 @@
|
||||
handleError(error, 'Unable to connect!');
|
||||
}
|
||||
|
||||
loading = false;
|
||||
oauthLoading = false;
|
||||
});
|
||||
|
||||
const login = async () => {
|
||||
@ -75,100 +75,89 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="border bg-white dark:bg-immich-dark-gray dark:border-immich-dark-gray p-8 shadow-sm w-full max-w-lg rounded-3xl"
|
||||
>
|
||||
<div class="flex flex-col place-items-center place-content-center gap-4 py-4">
|
||||
<ImmichLogo class="text-center h-24 w-24" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">Login</h1>
|
||||
</div>
|
||||
|
||||
{#if loginPageMessage}
|
||||
<p
|
||||
class="text-sm border rounded-xl p-4 text-immich-primary dark:text-immich-dark-primary font-medium bg-immich-primary/5 dark:border-immich-dark-bg w-full border-immich-primary border-2"
|
||||
>
|
||||
{@html loginPageMessage}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{#if authConfig.passwordLoginEnabled}
|
||||
<form on:submit|preventDefault={login} class="flex flex-col gap-5 mt-5">
|
||||
{#if error}
|
||||
<p class="text-red-400" transition:fade>
|
||||
{error}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Email</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
bind:value={email}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="password">Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
bind:value={password}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="my-5 flex w-full">
|
||||
<button
|
||||
type="submit"
|
||||
class="immich-btn-primary-big inline-flex items-center h-14"
|
||||
disabled={loading}
|
||||
>
|
||||
{#if loading}
|
||||
<LoadingSpinner />
|
||||
{:else}
|
||||
Login
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
{#if authConfig.enabled}
|
||||
{#if authConfig.passwordLoginEnabled}
|
||||
<div class="inline-flex items-center justify-center w-full">
|
||||
<hr class="w-3/4 h-px my-4 bg-gray-200 border-0 dark:bg-gray-600" />
|
||||
<span
|
||||
class="absolute px-3 font-medium text-gray-900 -translate-x-1/2 left-1/2 dark:text-white bg-white dark:bg-immich-dark-gray"
|
||||
>
|
||||
or
|
||||
</span>
|
||||
</div>
|
||||
{#if authConfig.passwordLoginEnabled}
|
||||
<form on:submit|preventDefault={login} class="flex flex-col gap-5 mt-5">
|
||||
{#if error}
|
||||
<p class="text-red-400" transition:fade>
|
||||
{error}
|
||||
</p>
|
||||
{/if}
|
||||
<div class="my-5 flex flex-col gap-5">
|
||||
{#if oauthError}
|
||||
<p class="text-red-400" transition:fade>{oauthError}</p>
|
||||
{/if}
|
||||
<a href={authConfig.url} class="flex w-full">
|
||||
<button
|
||||
type="button"
|
||||
disabled={loading}
|
||||
class={authConfig.passwordLoginEnabled
|
||||
? 'immich-btn-secondary-big'
|
||||
: 'immich-btn-primary-big'}
|
||||
>
|
||||
{authConfig.buttonText || 'Login with OAuth'}
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Email</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
bind:value={email}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="password">Password</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
bind:value={password}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="my-5 flex w-full">
|
||||
<button
|
||||
type="submit"
|
||||
class="immich-btn-primary-big inline-flex items-center h-14"
|
||||
disabled={loading || oauthLoading}
|
||||
>
|
||||
{#if loading}
|
||||
<LoadingSpinner />
|
||||
{:else}
|
||||
Login
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
{#if authConfig.enabled}
|
||||
{#if authConfig.passwordLoginEnabled}
|
||||
<div class="inline-flex items-center justify-center w-full">
|
||||
<hr class="w-3/4 h-px my-4 bg-gray-200 border-0 dark:bg-gray-600" />
|
||||
<span
|
||||
class="absolute px-3 font-medium text-gray-900 -translate-x-1/2 left-1/2 dark:text-white bg-white dark:bg-immich-dark-gray"
|
||||
>
|
||||
or
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="my-5 flex flex-col gap-5">
|
||||
{#if oauthError}
|
||||
<p class="text-red-400" transition:fade>{oauthError}</p>
|
||||
{/if}
|
||||
<a href={authConfig.url} class="flex w-full">
|
||||
<button
|
||||
type="button"
|
||||
disabled={loading || oauthLoading}
|
||||
class={'inline-flex items-center h-14 ' + authConfig.passwordLoginEnabled
|
||||
? 'immich-btn-secondary-big'
|
||||
: 'immich-btn-primary-big'}
|
||||
>
|
||||
{#if oauthLoading}
|
||||
<LoadingSpinner />
|
||||
{:else}
|
||||
{authConfig.buttonText || 'Login with OAuth'}
|
||||
{/if}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !authConfig.enabled && !authConfig.passwordLoginEnabled}
|
||||
<p class="text-center dark:text-immich-dark-fg p-4">Login has been disabled.</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if !authConfig.enabled && !authConfig.passwordLoginEnabled}
|
||||
<p class="text-center dark:text-immich-dark-fg p-4">Login has been disabled.</p>
|
||||
{/if}
|
||||
|
@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import ImmichLogo from './immich-logo.svelte';
|
||||
|
||||
export let title: string;
|
||||
export let showMessage = $$slots.message;
|
||||
</script>
|
||||
|
||||
<section class="min-h-screen w-screen flex place-items-center place-content-center p-4">
|
||||
<div
|
||||
class="flex flex-col gap-4 border bg-white dark:bg-immich-dark-gray dark:border-immich-dark-gray p-8 shadow-sm w-full max-w-lg rounded-3xl"
|
||||
>
|
||||
<div class="flex flex-col place-items-center place-content-center gap-4 py-4">
|
||||
<ImmichLogo class="h-24 w-24" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
{title}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{#if showMessage}
|
||||
<div
|
||||
class="text-sm border rounded-xl p-4 text-immich-primary dark:text-immich-dark-primary font-medium bg-immich-primary/5 dark:border-immich-dark-bg w-full border-immich-primary border-2"
|
||||
>
|
||||
<slot name="message" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
@ -14,5 +14,8 @@ export enum AppRoute {
|
||||
SHARING = '/sharing',
|
||||
SEARCH = '/search',
|
||||
|
||||
AUTH_LOGIN = '/auth/login'
|
||||
AUTH_LOGIN = '/auth/login',
|
||||
AUTH_LOGOUT = '/auth/logout',
|
||||
AUTH_REGISTER = '/auth/register',
|
||||
AUTH_CHANGE_PASSWORD = '/auth/change-password'
|
||||
}
|
||||
|
@ -1,23 +1,18 @@
|
||||
export const prerender = false;
|
||||
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load = (async ({ locals: { api } }) => {
|
||||
try {
|
||||
const { data: userInfo } = await api.userApi.getMyUserInfo();
|
||||
|
||||
if (userInfo.shouldChangePassword) {
|
||||
return {
|
||||
user: userInfo,
|
||||
meta: {
|
||||
title: 'Change Password'
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw redirect(302, '/photos');
|
||||
}
|
||||
} catch (e) {
|
||||
throw redirect(302, '/auth/login');
|
||||
export const load = (async ({ locals: { user } }) => {
|
||||
if (!user) {
|
||||
throw redirect(302, AppRoute.AUTH_LOGIN);
|
||||
} else if (!user.shouldChangePassword) {
|
||||
throw redirect(302, AppRoute.PHOTOS);
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
meta: {
|
||||
title: 'Change Password'
|
||||
}
|
||||
};
|
||||
}) satisfies PageServerLoad;
|
||||
|
@ -1,21 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
import ChangePasswordForm from '$lib/components/forms/change-password-form.svelte';
|
||||
import FullscreenContainer from '$lib/components/shared-components/fullscreen-container.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
const onSuccessHandler = async () => {
|
||||
await fetch('auth/logout', { method: 'POST' });
|
||||
await fetch(AppRoute.AUTH_LOGOUT, { method: 'POST' });
|
||||
|
||||
goto('/auth/login');
|
||||
goto(AppRoute.AUTH_LOGIN);
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="h-screen w-screen flex place-items-center place-content-center">
|
||||
<div in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}>
|
||||
<ChangePasswordForm user={data.user} on:success={onSuccessHandler} />
|
||||
</div>
|
||||
</section>
|
||||
<FullscreenContainer title={data.meta.title}>
|
||||
<p slot="message">
|
||||
Hi {data.user.firstName}
|
||||
{data.user.lastName} ({data.user.email}),
|
||||
<br />
|
||||
<br />
|
||||
This is either the first time you are signing into the system or a request has been made to change
|
||||
your password. Please enter the new password below.
|
||||
</p>
|
||||
|
||||
<ChangePasswordForm user={data.user} on:success={onSuccessHandler} />
|
||||
</FullscreenContainer>
|
||||
|
@ -1,14 +1,32 @@
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { OAuthConfigResponseDto } from '@api';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load = (async ({ locals: { api } }) => {
|
||||
const { data } = await api.userApi.getUserCount(true);
|
||||
if (data.userCount === 0) {
|
||||
// Admin not registered
|
||||
throw redirect(302, '/auth/register');
|
||||
throw redirect(302, AppRoute.AUTH_REGISTER);
|
||||
}
|
||||
|
||||
let authConfig: OAuthConfigResponseDto = {
|
||||
passwordLoginEnabled: true,
|
||||
enabled: false
|
||||
};
|
||||
|
||||
try {
|
||||
// TODO: Figure out how to get correct redirect URI server-side.
|
||||
const { data } = await api.oauthApi.generateConfig({ redirectUri: '/' });
|
||||
data.url = undefined;
|
||||
|
||||
authConfig = data;
|
||||
} catch (err) {
|
||||
console.error('[ERROR] login/+page.server.ts:', err);
|
||||
}
|
||||
|
||||
return {
|
||||
authConfig,
|
||||
meta: {
|
||||
title: 'Login'
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
import LoginForm from '$lib/components/forms/login-form.svelte';
|
||||
import FullscreenContainer from '$lib/components/shared-components/fullscreen-container.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { loginPageMessage } from '$lib/constants';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<section
|
||||
class="min-h-screen w-screen flex place-items-center place-content-center p-4"
|
||||
transition:fade={{ duration: 100 }}
|
||||
>
|
||||
<FullscreenContainer title={data.meta.title} showMessage={!!loginPageMessage}>
|
||||
<p slot="message">
|
||||
{@html loginPageMessage}
|
||||
</p>
|
||||
|
||||
<LoginForm
|
||||
on:success={() => goto('/photos')}
|
||||
on:first-login={() => goto('/auth/change-password')}
|
||||
authConfig={data.authConfig}
|
||||
on:success={() => goto(AppRoute.PHOTOS, { invalidateAll: true })}
|
||||
on:first-login={() => goto(AppRoute.AUTH_CHANGE_PASSWORD)}
|
||||
/>
|
||||
</section>
|
||||
</FullscreenContainer>
|
||||
|
@ -1,7 +1,16 @@
|
||||
<script lang="ts">
|
||||
import AdminRegistrationForm from '$lib/components/forms/admin-registration-form.svelte';
|
||||
import FullscreenContainer from '$lib/components/shared-components/fullscreen-container.svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<section class="h-screen w-screen flex place-items-center place-content-center">
|
||||
<FullscreenContainer title={data.meta.title}>
|
||||
<p slot="message">
|
||||
Since you are the first user on the system, you will be assigned as the Admin and are
|
||||
responsible for administrative tasks, and additional users will be created by you.
|
||||
</p>
|
||||
|
||||
<AdminRegistrationForm />
|
||||
</section>
|
||||
</FullscreenContainer>
|
||||
|
Loading…
Reference in New Issue
Block a user