mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-02-12 05:09:00 +00:00
feat: improve initial admin creation workflow
This commit is contained in:
@@ -114,6 +114,11 @@ export default class UserService extends APIService {
|
||||
return res.data as User;
|
||||
}
|
||||
|
||||
async signupInitialUser(data: UserSignUp) {
|
||||
const res = await this.api.post(`/signup/setup`, data);
|
||||
return res.data as User;
|
||||
}
|
||||
|
||||
async listSignupTokens(options?: SearchPaginationSortRequest) {
|
||||
const res = await this.api.get('/signup-tokens', {
|
||||
params: options
|
||||
|
||||
@@ -12,7 +12,7 @@ export function getAuthRedirectPath(path: string, user: User | null) {
|
||||
path == '/lc' ||
|
||||
path.startsWith('/lc/') ||
|
||||
path == '/signup' ||
|
||||
path.startsWith('/signup/') ||
|
||||
path == '/signup/setup' ||
|
||||
path.startsWith('/st/');
|
||||
const isPublicPath = ['/authorize', '/device', '/health', '/healthz'].includes(path);
|
||||
const isAdminPath = path == '/settings/admin' || path.startsWith('/settings/admin/');
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import SignInWrapper from '$lib/components/login-wrapper.svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import UserService from '$lib/services/user-service';
|
||||
import appConfigStore from '$lib/stores/application-configuration-store.js';
|
||||
import userStore from '$lib/stores/user-store.js';
|
||||
import { getAxiosErrorMessage } from '$lib/utils/error-util';
|
||||
import LoginLogoErrorSuccessIndicator from '../components/login-logo-error-success-indicator.svelte';
|
||||
|
||||
let isLoading = $state(false);
|
||||
let error: string | undefined = $state();
|
||||
|
||||
const userService = new UserService();
|
||||
|
||||
async function authenticate() {
|
||||
isLoading = true;
|
||||
try {
|
||||
const user = await userService.exchangeOneTimeAccessToken('setup');
|
||||
userStore.setUser(user);
|
||||
|
||||
goto('/settings');
|
||||
} catch (e) {
|
||||
error = getAxiosErrorMessage(e);
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<SignInWrapper animate={!$appConfigStore.disableAnimations}>
|
||||
<div class="flex justify-center">
|
||||
<LoginLogoErrorSuccessIndicator error={!!error} />
|
||||
</div>
|
||||
<h1 class="font-playfair mt-5 text-4xl font-bold">
|
||||
{m.appname_setup({ appName: $appConfigStore.appName })}
|
||||
</h1>
|
||||
{#if error}
|
||||
<p class="text-muted-foreground mt-2">
|
||||
{error}. {m.please_try_again()}
|
||||
</p>
|
||||
{:else}
|
||||
<p class="text-muted-foreground mt-2">
|
||||
{m.you_are_about_to_sign_in_to_the_initial_admin_account()}
|
||||
</p>
|
||||
<Button class="mt-5" {isLoading} onclick={authenticate}>{m.continue()}</Button>
|
||||
{/if}
|
||||
</SignInWrapper>
|
||||
5
frontend/src/routes/setup/+page.ts
Normal file
5
frontend/src/routes/setup/+page.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
// Alias for /signup/setup
|
||||
export const load: PageLoad = async () => redirect(307, '/signup/setup');
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { openConfirmDialog } from '$lib/components/confirm-dialog';
|
||||
import SignInWrapper from '$lib/components/login-wrapper.svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
@@ -44,6 +45,20 @@
|
||||
goto('/settings/account');
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
function skipForNow() {
|
||||
openConfirmDialog({
|
||||
title: m.skip_passkey_setup(),
|
||||
message: m.skip_passkey_setup_description(),
|
||||
confirm: {
|
||||
label: m.skip_for_now(),
|
||||
destructive: true,
|
||||
action: () => {
|
||||
goto('/settings/account');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -66,12 +81,7 @@
|
||||
{/if}
|
||||
</p>
|
||||
<div class="mt-10 flex w-full justify-between gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onclick={() => goto('/settings/account')}
|
||||
disabled={isLoading}
|
||||
class="flex-1"
|
||||
>
|
||||
<Button variant="secondary" onclick={skipForNow} disabled={isLoading} class="flex-1">
|
||||
{m.skip_for_now()}
|
||||
</Button>
|
||||
<Button onclick={createPasskeyAndContinue} {isLoading} class="flex-1">
|
||||
|
||||
70
frontend/src/routes/signup/setup/+page.svelte
Normal file
70
frontend/src/routes/signup/setup/+page.svelte
Normal file
@@ -0,0 +1,70 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import SignInWrapper from '$lib/components/login-wrapper.svelte';
|
||||
import SignupForm from '$lib/components/signup/signup-form.svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import UserService from '$lib/services/user-service';
|
||||
import appConfigStore from '$lib/stores/application-configuration-store';
|
||||
import userStore from '$lib/stores/user-store';
|
||||
import type { UserSignUp } from '$lib/types/user.type';
|
||||
import { getAxiosErrorMessage } from '$lib/utils/error-util';
|
||||
import { tryCatch } from '$lib/utils/try-catch-util';
|
||||
import { fade } from 'svelte/transition';
|
||||
import LoginLogoErrorSuccessIndicator from '../../login/components/login-logo-error-success-indicator.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
const userService = new UserService();
|
||||
|
||||
let isLoading = $state(false);
|
||||
let error: string | undefined = $state();
|
||||
|
||||
async function handleSignup(userData: UserSignUp) {
|
||||
isLoading = true;
|
||||
|
||||
const result = await tryCatch(userService.signupInitialUser(userData));
|
||||
|
||||
if (result.error) {
|
||||
error = getAxiosErrorMessage(result.error);
|
||||
isLoading = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
userStore.setUser(result.data);
|
||||
isLoading = false;
|
||||
|
||||
goto('/signup/add-passkey');
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{m.signup()}</title>
|
||||
</svelte:head>
|
||||
|
||||
<SignInWrapper animate={!$appConfigStore.disableAnimations}>
|
||||
<div class="flex justify-center">
|
||||
<LoginLogoErrorSuccessIndicator error={!!error} />
|
||||
</div>
|
||||
|
||||
<h1 class="font-playfair mt-5 text-3xl font-bold sm:text-4xl">
|
||||
{m.signup_to_appname({ appName: $appConfigStore.appName })}
|
||||
</h1>
|
||||
|
||||
{#if !error}
|
||||
<p class="text-muted-foreground mt-2" in:fade>
|
||||
{m.initial_account_creation_description()}
|
||||
</p>
|
||||
{:else}
|
||||
<p class="text-muted-foreground mt-2" in:fade>
|
||||
{error}.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<SignupForm callback={handleSignup} {isLoading} />
|
||||
<div class="mt-10 flex w-full justify-end">
|
||||
<Button type="submit" form="sign-up-form" onclick={() => (error = undefined)}
|
||||
>{m.signup()}</Button
|
||||
>
|
||||
</div>
|
||||
</SignInWrapper>
|
||||
Reference in New Issue
Block a user