fix(web): login error handling (#1322)

This commit is contained in:
Jason Rasmussen 2023-01-13 17:04:59 -05:00 committed by GitHub
parent ba04b753de
commit 5fb3ea465f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 154 additions and 35 deletions

6
web/src/app.d.ts vendored
View file

@ -8,6 +8,12 @@ declare namespace App {
}
// interface Platform {}
interface Error {
message: string;
stack?: string;
code?: string;
}
}
// Source: https://stackoverflow.com/questions/63814432/typescript-typing-of-non-standard-window-event-in-svelte

View file

@ -1,5 +1,15 @@
import type { Handle } from '@sveltejs/kit';
import type { Handle, HandleServerError } from '@sveltejs/kit';
import { AxiosError } from 'axios';
export const handle: Handle = async ({ event, resolve }) => {
return await resolve(event);
};
export const handleError: HandleServerError = async ({ error }) => {
const httpError = error as AxiosError;
return {
message: httpError?.message || 'Hmm, not sure about that. Check the logs or open a ticket?',
stack: httpError?.stack,
code: httpError.code || '500'
};
};

View file

@ -2,6 +2,7 @@
import { goto } from '$app/navigation';
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
import { loginPageMessage } from '$lib/constants';
import { handleError } from '$lib/utils/handle-error';
import { api, oauth, OAuthConfigResponseDto } from '@api';
import { createEventDispatcher, onMount } from 'svelte';
@ -9,7 +10,7 @@
let email = '';
let password = '';
let oauthError: string;
let oauthConfig: OAuthConfigResponseDto = { enabled: false, passwordLoginEnabled: false };
let authConfig: OAuthConfigResponseDto = { enabled: false, passwordLoginEnabled: false };
let loading = true;
const dispatch = createEventDispatcher();
@ -30,17 +31,18 @@
try {
const { data } = await oauth.getConfig(window.location);
oauthConfig = data;
authConfig = data;
const { enabled, url, autoLaunch } = oauthConfig;
const { enabled, url, autoLaunch } = authConfig;
if (enabled && url && autoLaunch && !oauth.isAutoLaunchDisabled(window.location)) {
await goto('/auth/login?autoLaunch=0', { replaceState: true });
await goto(url);
return;
}
} catch (e) {
console.error('Error [login-form] [oauth.generateConfig]', e);
} catch (error) {
authConfig.passwordLoginEnabled = true;
handleError(error, 'Unable to connect!');
}
loading = false;
@ -92,7 +94,7 @@
<LoadingSpinner />
</div>
{:else}
{#if oauthConfig.passwordLoginEnabled}
{#if authConfig.passwordLoginEnabled}
<form on:submit|preventDefault={login} autocomplete="off">
<div class="m-4 flex flex-col gap-2">
<label class="immich-form-label" for="email">Email</label>
@ -133,26 +135,26 @@
</form>
{/if}
{#if oauthConfig.enabled}
{#if authConfig.enabled}
<div class="flex flex-col gap-4 px-4">
{#if oauthConfig.passwordLoginEnabled}
{#if authConfig.passwordLoginEnabled}
<hr />
{/if}
{#if oauthError}
<p class="text-red-400">{oauthError}</p>
{/if}
<a href={oauthConfig.url} class="flex w-full">
<a href={authConfig.url} class="flex w-full">
<button
type="button"
disabled={loading}
class="bg-immich-primary dark:bg-immich-dark-primary dark:text-immich-dark-gray dark:hover:bg-immich-dark-primary/80 hover:bg-immich-primary/75 px-6 py-4 text-white rounded-md shadow-md w-full font-semibold"
>{oauthConfig.buttonText || 'Login with OAuth'}</button
>{authConfig.buttonText || 'Login with OAuth'}</button
>
</a>
</div>
{/if}
{#if !oauthConfig.enabled && !oauthConfig.passwordLoginEnabled}
{#if !authConfig.enabled && !authConfig.passwordLoginEnabled}
<p class="text-center dark:text-immich-dark-fg p-4">Login has been disabled.</p>
{/if}
{/if}

View file

@ -90,5 +90,7 @@
</button>
</div>
<p class="text-sm pl-[28px] pr-[16px]" data-testid="message">{@html notificationInfo.message}</p>
<p class="whitespace-pre text-sm pl-[28px] pr-[16px]" data-testid="message">
{@html notificationInfo.message}
</p>
</div>

View file

@ -6,8 +6,14 @@ import {
export function handleError(error: unknown, message: string) {
console.error(`[handleError]: ${message}`, error);
let serverMessage = (error as ApiError)?.response?.data?.message;
if (serverMessage) {
serverMessage = `${String(serverMessage).slice(0, 50)}\n<i>(Immich Server Error)<i>`;
}
notificationController.show({
message: (error as ApiError)?.response?.data?.message || message,
message: serverMessage || message,
type: NotificationType.Error
});
}

View file

@ -1,29 +1,122 @@
<script>
import { page } from '$app/stores';
import Message from 'svelte-material-icons/Message.svelte';
import PartyPopper from 'svelte-material-icons/PartyPopper.svelte';
import CodeTags from 'svelte-material-icons/CodeTags.svelte';
import ContentCopy from 'svelte-material-icons/ContentCopy.svelte';
import {
notificationController,
NotificationType
} from '$lib/components/shared-components/notification/notification';
import { handleError } from '$lib/utils/handle-error';
const handleCopy = async () => {
//
const error = $page.error || null;
if (!error) {
return;
}
try {
await navigator.clipboard.writeText(`${error.message} - ${error.code}\n${error.stack}`);
notificationController.show({
type: NotificationType.Info,
message: 'Copied error to clipboard'
});
} catch (error) {
handleError(error, 'Unable to copy to clipboard');
}
};
</script>
<div class="h-screen w-screen flex place-items-center place-content-center flex-col">
<div class="min-w-[500px] max-w-[95vw] bg-gray-300 rounded-2xl my-4 p-4">
<code class="text-xs text-red-500">Error code {$page.status}</code>
<br />
<code class="text-sm">
{$page.error?.message}
</code>
<br />
<div class="mt-5">
<p class="text-sm font-medium">Verbose</p>
<pre class="text-xs">{JSON.stringify($page.error)}</pre>
<div class="h-screen w-screen">
<section class="bg-immich-bg dark:bg-immich-dark-bg">
<div class="flex border-b dark:border-b-immich-dark-gray place-items-center px-6 py-4">
<a class="flex gap-2 place-items-center hover:cursor-pointer" href="/photos">
<img src="/immich-logo.svg" alt="immich logo" height="35" width="35" />
<h1 class="font-immich-title text-2xl text-immich-primary dark:text-immich-dark-primary">
IMMICH
</h1>
</a>
</div>
</section>
<a
href="https://github.com/immich-app/immich/issues/new/choose"
target="_blank"
rel="noopener noreferrer"
>
<button
class="px-5 py-2 rounded-lg text-sm mt-6 bg-immich-primary text-white hover:bg-immich-primary/75"
>Get help</button
<div
class="fixed top-0 w-full h-full bg-black/50 flex place-items-center place-content-center overflow-hidden"
>
<div>
<div
class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray shadow-sm w-[500px] max-w-[95vw] rounded-3xl dark:text-immich-dark-fg"
>
</a>
<div>
<div class="flex items-center justify-between gap-4 px-4 py-4">
<h1 class="text-immich-primary dark:text-immich-dark-primary font-medium">
🚨 Error - Something went wrong
</h1>
<div class="flex justify-end">
<button
on:click={() => handleCopy()}
class="transition-colors 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-3 py-2 text-white rounded-full shadow-md text-sm"
>
<ContentCopy size={24} />
</button>
</div>
</div>
<hr />
<div class="p-4 max-h-[75vh] min-h-[300px] overflow-y-auto immich-scrollbar pb-4 gap-4">
<div class="flex flex-col w-full gap-2">
<p class="text-red-500">{$page.error?.message} - {$page.error?.code}</p>
{#if $page.error?.stack}
<label for="stacktrace">Stacktrace</label>
<pre id="stacktrace" class="text-xs">{$page.error?.stack || 'No stack'}</pre>
{/if}
</div>
</div>
<hr />
<div class="flex justify-around place-items-center place-content-center">
<!-- href="https://github.com/immich-app/immich/issues/new" -->
<a
href="https://discord.com/invite/D8JsnBEuKb"
target="_blank"
rel="noopener noreferrer"
class="flex justify-center grow basis-0 p-4"
>
<button class="flex flex-col gap-2 place-items-center place-content-center">
<Message size={24} />
<p class="text-sm">Get Help</p>
</button>
</a>
<a
href="https://github.com/immich-app/immich/releases"
target="_blank"
rel="noopener noreferrer"
class="flex justify-center grow basis-0 p-4"
>
<button class="flex flex-col gap-2 place-items-center place-content-center">
<PartyPopper size={24} />
<p class="text-sm">Read Changelog</p>
</button>
</a>
<a
href="https://immich.app/docs/guides/docker-help"
target="_blank"
rel="noopener noreferrer"
class="flex justify-center grow basis-0 p-4"
>
<button class="flex flex-col gap-2 place-items-center place-content-center">
<CodeTags size={24} />
<p class="text-sm">Check Logs</p>
</button>
</a>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -78,7 +78,7 @@
</script>
<svelte:head>
<title>{$page.data.meta?.title} - Immich</title>
<title>{$page.data.meta?.title || 'Web'} - Immich</title>
{#if $page.data.meta}
<meta name="description" content={$page.data.meta.description} />