mirror of
https://github.com/nikdoof/pocket-id.git
synced 2025-12-22 13:59:24 +00:00
feat: allow sign in with email (#100)
This commit is contained in:
@@ -2,22 +2,38 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { browserSupportsWebAuthn } from '@simplewebauthn/browser';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { Button } from './ui/button';
|
||||
import * as Card from './ui/card';
|
||||
import WebAuthnUnsupported from './web-authn-unsupported.svelte';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
let {
|
||||
children
|
||||
children,
|
||||
showEmailOneTimeAccessButton = false
|
||||
}: {
|
||||
children: Snippet;
|
||||
showEmailOneTimeAccessButton?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<!-- Desktop -->
|
||||
<div class="hidden h-screen items-center text-center lg:flex">
|
||||
<div class="min-w-[650px] p-16">
|
||||
<div class="h-full min-w-[650px] p-16 {showEmailOneTimeAccessButton ? 'pb-0' : ''}">
|
||||
{#if browser && !browserSupportsWebAuthn()}
|
||||
<WebAuthnUnsupported />
|
||||
{:else}
|
||||
{@render children()}
|
||||
<div class="flex h-full flex-col">
|
||||
<div class="flex flex-grow flex-col items-center justify-center">
|
||||
{@render children()}
|
||||
</div>
|
||||
{#if showEmailOneTimeAccessButton}
|
||||
<div class="mb-4 flex justify-center">
|
||||
<Button href="/login/email?redirect={encodeURIComponent($page.url.pathname + $page.url.search)}" variant="link" class="text-muted-foreground text-xs">
|
||||
Don't have access to your passkey?
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<img
|
||||
@@ -27,15 +43,25 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Mobile -->
|
||||
<div
|
||||
class="flex h-screen items-center justify-center bg-[url('/api/application-configuration/background-image')] bg-cover bg-center text-center lg:hidden"
|
||||
>
|
||||
<Card.Root class="mx-3">
|
||||
<Card.CardContent class="px-4 py-10 sm:p-10">
|
||||
<Card.CardContent
|
||||
class="px-4 py-10 sm:p-10 {showEmailOneTimeAccessButton ? 'pb-3 sm:pb-3' : ''}"
|
||||
>
|
||||
{#if browser && !browserSupportsWebAuthn()}
|
||||
<WebAuthnUnsupported />
|
||||
{:else}
|
||||
{@render children()}
|
||||
{#if showEmailOneTimeAccessButton}
|
||||
<div class="mt-5">
|
||||
<Button href="/login/email?redirect={encodeURIComponent($page.url.pathname + $page.url.search)}" variant="link" class="text-muted-foreground text-xs">
|
||||
Don't have access to your passkey?
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</Card.CardContent>
|
||||
</Card.Root>
|
||||
|
||||
@@ -11,13 +11,7 @@ export default class AppConfigService extends APIService {
|
||||
}
|
||||
|
||||
const { data } = await this.api.get<AppConfigRawResponse>(url);
|
||||
|
||||
const appConfig: Partial<AllAppConfig> = {};
|
||||
data.forEach(({ key, value }) => {
|
||||
(appConfig as any)[key] = this.parseValue(value);
|
||||
});
|
||||
|
||||
return appConfig as AllAppConfig;
|
||||
return this.parseConfigList(data);
|
||||
}
|
||||
|
||||
async update(appConfig: AllAppConfig) {
|
||||
@@ -27,7 +21,7 @@ export default class AppConfigService extends APIService {
|
||||
(appConfigConvertedToString as any)[key] = (appConfig as any)[key].toString();
|
||||
}
|
||||
const res = await this.api.put('/application-configuration', appConfigConvertedToString);
|
||||
return res.data as AllAppConfig;
|
||||
return this.parseConfigList(res.data);
|
||||
}
|
||||
|
||||
async updateFavicon(favicon: File) {
|
||||
@@ -76,6 +70,15 @@ export default class AppConfigService extends APIService {
|
||||
};
|
||||
}
|
||||
|
||||
private parseConfigList(data: AppConfigRawResponse) {
|
||||
const appConfig: Partial<AllAppConfig> = {};
|
||||
data.forEach(({ key, value }) => {
|
||||
(appConfig as any)[key] = this.parseValue(value);
|
||||
});
|
||||
|
||||
return appConfig as AllAppConfig;
|
||||
}
|
||||
|
||||
private parseValue(value: string) {
|
||||
if (value === 'true') {
|
||||
return true;
|
||||
|
||||
@@ -51,4 +51,8 @@ export default class UserService extends APIService {
|
||||
const res = await this.api.post(`/one-time-access-token/${token}`);
|
||||
return res.data as User;
|
||||
}
|
||||
|
||||
async requestOneTimeAccessEmail(email: string, redirectPath?: string) {
|
||||
await this.api.post('/one-time-access-email', { email, redirectPath });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export type AppConfig = {
|
||||
appName: string;
|
||||
allowOwnAccountEdit: boolean;
|
||||
emailOneTimeAccessEnabled: boolean
|
||||
};
|
||||
|
||||
export type AllAppConfig = AppConfig & {
|
||||
@@ -8,7 +9,6 @@ export type AllAppConfig = AppConfig & {
|
||||
sessionDuration: number;
|
||||
emailsVerified: boolean;
|
||||
// Email
|
||||
emailEnabled: boolean;
|
||||
smtpHost: string;
|
||||
smtpPort: number;
|
||||
smtpFrom: string;
|
||||
@@ -16,6 +16,7 @@ export type AllAppConfig = AppConfig & {
|
||||
smtpPassword: string;
|
||||
smtpTls: boolean;
|
||||
smtpSkipCertVerify: boolean;
|
||||
emailLoginNotificationEnabled: boolean;
|
||||
// LDAP
|
||||
ldapEnabled: boolean;
|
||||
ldapUrl: string;
|
||||
|
||||
@@ -2,10 +2,19 @@ import { WebAuthnError } from '@simplewebauthn/browser';
|
||||
import { AxiosError } from 'axios';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
export function axiosErrorToast(e: unknown, message: string = 'An unknown error occurred') {
|
||||
export function getAxiosErrorMessage(
|
||||
e: unknown,
|
||||
defaultMessage: string = 'An unknown error occurred'
|
||||
) {
|
||||
let message = defaultMessage;
|
||||
if (e instanceof AxiosError) {
|
||||
message = e.response?.data.error || message;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
export function axiosErrorToast(e: unknown, defaultMessage: string = 'An unknown error occurred') {
|
||||
const message = getAxiosErrorMessage(e, defaultMessage);
|
||||
toast.error(message);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user