mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
parent
7c2f7d6c51
commit
f55b3add80
242 changed files with 12794 additions and 13426 deletions
|
|
@ -1,123 +1,102 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { api } from '@api';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { api } from '@api';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
|
||||
let error: string;
|
||||
let password = '';
|
||||
let confirmPassowrd = '';
|
||||
let canRegister = false;
|
||||
let error: string;
|
||||
let password = '';
|
||||
let confirmPassowrd = '';
|
||||
let canRegister = false;
|
||||
|
||||
$: {
|
||||
if (password !== confirmPassowrd && confirmPassowrd.length > 0) {
|
||||
error = 'Password does not match';
|
||||
canRegister = false;
|
||||
} else {
|
||||
error = '';
|
||||
canRegister = true;
|
||||
}
|
||||
}
|
||||
$: {
|
||||
if (password !== confirmPassowrd && confirmPassowrd.length > 0) {
|
||||
error = 'Password does not match';
|
||||
canRegister = false;
|
||||
} else {
|
||||
error = '';
|
||||
canRegister = true;
|
||||
}
|
||||
}
|
||||
|
||||
async function registerAdmin(event: SubmitEvent & { currentTarget: HTMLFormElement }) {
|
||||
if (canRegister) {
|
||||
error = '';
|
||||
async function registerAdmin(event: SubmitEvent & { currentTarget: HTMLFormElement }) {
|
||||
if (canRegister) {
|
||||
error = '';
|
||||
|
||||
const form = new FormData(event.currentTarget);
|
||||
const form = new FormData(event.currentTarget);
|
||||
|
||||
const email = form.get('email');
|
||||
const password = form.get('password');
|
||||
const firstName = form.get('firstName');
|
||||
const lastName = form.get('lastName');
|
||||
const email = form.get('email');
|
||||
const password = form.get('password');
|
||||
const firstName = form.get('firstName');
|
||||
const lastName = form.get('lastName');
|
||||
|
||||
const { status } = await api.authenticationApi.adminSignUp({
|
||||
signUpDto: {
|
||||
email: String(email),
|
||||
password: String(password),
|
||||
firstName: String(firstName),
|
||||
lastName: String(lastName)
|
||||
}
|
||||
});
|
||||
const { status } = await api.authenticationApi.adminSignUp({
|
||||
signUpDto: {
|
||||
email: String(email),
|
||||
password: String(password),
|
||||
firstName: String(firstName),
|
||||
lastName: String(lastName),
|
||||
},
|
||||
});
|
||||
|
||||
if (status === 201) {
|
||||
goto(AppRoute.AUTH_LOGIN);
|
||||
return;
|
||||
} else {
|
||||
error = 'Error create admin account';
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status === 201) {
|
||||
goto(AppRoute.AUTH_LOGIN);
|
||||
return;
|
||||
} else {
|
||||
error = 'Error create admin account';
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<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="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="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="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="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="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="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="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>
|
||||
|
||||
{#if error}
|
||||
<p class="text-red-400">{error}</p>
|
||||
{/if}
|
||||
{#if error}
|
||||
<p class="text-red-400">{error}</p>
|
||||
{/if}
|
||||
|
||||
<div class="my-5 flex w-full">
|
||||
<Button type="submit" size="lg" fullwidth>Sign up</Button>
|
||||
</div>
|
||||
<div class="my-5 flex w-full">
|
||||
<Button type="submit" size="lg" fullwidth>Sign up</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,49 +1,43 @@
|
|||
<script lang="ts">
|
||||
import type { APIKeyResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import KeyVariant from 'svelte-material-icons/KeyVariant.svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import FullScreenModal from '../shared-components/full-screen-modal.svelte';
|
||||
import type { APIKeyResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import KeyVariant from 'svelte-material-icons/KeyVariant.svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import FullScreenModal from '../shared-components/full-screen-modal.svelte';
|
||||
|
||||
export let apiKey: Partial<APIKeyResponseDto>;
|
||||
export let title = 'API Key';
|
||||
export let cancelText = 'Cancel';
|
||||
export let submitText = 'Save';
|
||||
export let apiKey: Partial<APIKeyResponseDto>;
|
||||
export let title = 'API Key';
|
||||
export let cancelText = 'Cancel';
|
||||
export let submitText = 'Save';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const handleCancel = () => dispatch('cancel');
|
||||
const handleSubmit = () => dispatch('submit', { ...apiKey, name: apiKey.name });
|
||||
const dispatch = createEventDispatcher();
|
||||
const handleCancel = () => dispatch('cancel');
|
||||
const handleSubmit = () => dispatch('submit', { ...apiKey, name: apiKey.name });
|
||||
</script>
|
||||
|
||||
<FullScreenModal on:clickOutside={() => handleCancel()}>
|
||||
<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 text-immich-primary dark:text-immich-dark-primary"
|
||||
>
|
||||
<KeyVariant size="4em" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
{title}
|
||||
</h1>
|
||||
</div>
|
||||
<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 text-immich-primary dark:text-immich-dark-primary"
|
||||
>
|
||||
<KeyVariant size="4em" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
{title}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off">
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Name</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
bind:value={apiKey.name}
|
||||
/>
|
||||
</div>
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off">
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Name</label>
|
||||
<input class="immich-form-input" id="name" name="name" type="text" bind:value={apiKey.name} />
|
||||
</div>
|
||||
|
||||
<div class="flex w-full px-4 gap-4 mt-8">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>{cancelText}</Button>
|
||||
<Button type="submit" fullwidth>{submitText}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="flex w-full px-4 gap-4 mt-8">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>{cancelText}</Button>
|
||||
<Button type="submit" fullwidth>{submitText}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</FullScreenModal>
|
||||
|
|
|
|||
|
|
@ -1,70 +1,59 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import KeyVariant from 'svelte-material-icons/KeyVariant.svelte';
|
||||
import { handleError } from '../../utils/handle-error';
|
||||
import FullScreenModal from '../shared-components/full-screen-modal.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType
|
||||
} from '../shared-components/notification/notification';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import KeyVariant from 'svelte-material-icons/KeyVariant.svelte';
|
||||
import { handleError } from '../../utils/handle-error';
|
||||
import FullScreenModal from '../shared-components/full-screen-modal.svelte';
|
||||
import { notificationController, NotificationType } from '../shared-components/notification/notification';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
|
||||
export let secret = '';
|
||||
export let secret = '';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const handleDone = () => dispatch('done');
|
||||
let canCopyImagesToClipboard = true;
|
||||
const dispatch = createEventDispatcher();
|
||||
const handleDone = () => dispatch('done');
|
||||
let canCopyImagesToClipboard = true;
|
||||
|
||||
onMount(async () => {
|
||||
const module = await import('copy-image-clipboard');
|
||||
canCopyImagesToClipboard = module.canCopyImagesToClipboard();
|
||||
});
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(secret);
|
||||
notificationController.show({
|
||||
message: 'Copied to clipboard!',
|
||||
type: NotificationType.Info
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to copy to clipboard');
|
||||
}
|
||||
};
|
||||
onMount(async () => {
|
||||
const module = await import('copy-image-clipboard');
|
||||
canCopyImagesToClipboard = module.canCopyImagesToClipboard();
|
||||
});
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(secret);
|
||||
notificationController.show({
|
||||
message: 'Copied to clipboard!',
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to copy to clipboard');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal>
|
||||
<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 text-immich-primary dark:text-immich-dark-primary"
|
||||
>
|
||||
<KeyVariant size="4em" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
API Key
|
||||
</h1>
|
||||
<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 text-immich-primary dark:text-immich-dark-primary"
|
||||
>
|
||||
<KeyVariant size="4em" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">API Key</h1>
|
||||
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
This value will only be shown once. Please be sure to copy it before closing the window.
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
This value will only be shown once. Please be sure to copy it before closing the window.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<!-- <label class="immich-form-label" for="email">API Key</label> -->
|
||||
<textarea
|
||||
class="immich-form-input"
|
||||
id="secret"
|
||||
name="secret"
|
||||
readonly={true}
|
||||
value={secret}
|
||||
/>
|
||||
</div>
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<!-- <label class="immich-form-label" for="email">API Key</label> -->
|
||||
<textarea class="immich-form-input" id="secret" name="secret" readonly={true} value={secret} />
|
||||
</div>
|
||||
|
||||
<div class="flex w-full px-4 gap-4 mt-8">
|
||||
{#if canCopyImagesToClipboard}
|
||||
<Button on:click={() => handleCopy()} fullwidth>Copy to Clipboard</Button>
|
||||
{/if}
|
||||
<Button on:click={() => handleDone()} fullwidth>Done</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full px-4 gap-4 mt-8">
|
||||
{#if canCopyImagesToClipboard}
|
||||
<Button on:click={() => handleCopy()} fullwidth>Copy to Clipboard</Button>
|
||||
{/if}
|
||||
<Button on:click={() => handleDone()} fullwidth>Done</Button>
|
||||
</div>
|
||||
</div>
|
||||
</FullScreenModal>
|
||||
|
|
|
|||
|
|
@ -1,86 +1,86 @@
|
|||
<script lang="ts">
|
||||
import { api, UserResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import { api, UserResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
|
||||
export let user: UserResponseDto;
|
||||
let error: string;
|
||||
let success: string;
|
||||
export let user: UserResponseDto;
|
||||
let error: string;
|
||||
let success: string;
|
||||
|
||||
let password = '';
|
||||
let confirmPassowrd = '';
|
||||
let password = '';
|
||||
let confirmPassowrd = '';
|
||||
|
||||
let changeChagePassword = false;
|
||||
let changeChagePassword = false;
|
||||
|
||||
$: {
|
||||
if (password !== confirmPassowrd && confirmPassowrd.length > 0) {
|
||||
error = 'Password does not match';
|
||||
changeChagePassword = false;
|
||||
} else {
|
||||
error = '';
|
||||
changeChagePassword = true;
|
||||
}
|
||||
}
|
||||
$: {
|
||||
if (password !== confirmPassowrd && confirmPassowrd.length > 0) {
|
||||
error = 'Password does not match';
|
||||
changeChagePassword = false;
|
||||
} else {
|
||||
error = '';
|
||||
changeChagePassword = true;
|
||||
}
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
async function changePassword() {
|
||||
if (changeChagePassword) {
|
||||
error = '';
|
||||
async function changePassword() {
|
||||
if (changeChagePassword) {
|
||||
error = '';
|
||||
|
||||
const { status } = await api.userApi.updateUser({
|
||||
updateUserDto: {
|
||||
id: user.id,
|
||||
password: String(password),
|
||||
shouldChangePassword: false
|
||||
}
|
||||
});
|
||||
const { status } = await api.userApi.updateUser({
|
||||
updateUserDto: {
|
||||
id: user.id,
|
||||
password: String(password),
|
||||
shouldChangePassword: false,
|
||||
},
|
||||
});
|
||||
|
||||
if (status === 200) {
|
||||
dispatch('success');
|
||||
return;
|
||||
} else {
|
||||
console.error('Error changing password');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status === 200) {
|
||||
dispatch('success');
|
||||
return;
|
||||
} else {
|
||||
console.error('Error changing password');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<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="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>
|
||||
|
||||
{#if error}
|
||||
<p class="text-red-400 text-sm">{error}</p>
|
||||
{/if}
|
||||
{#if error}
|
||||
<p class="text-red-400 text-sm">{error}</p>
|
||||
{/if}
|
||||
|
||||
{#if success}
|
||||
<p class="text-immich-primary text-sm">{success}</p>
|
||||
{/if}
|
||||
<div class="my-5 flex w-full">
|
||||
<Button type="submit" size="lg" fullwidth>Change password</Button>
|
||||
</div>
|
||||
{#if success}
|
||||
<p class="text-immich-primary text-sm">{success}</p>
|
||||
{/if}
|
||||
<div class="my-5 flex w-full">
|
||||
<Button type="submit" size="lg" fullwidth>Change password</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,150 +1,135 @@
|
|||
<script lang="ts">
|
||||
import { api } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType
|
||||
} from '../shared-components/notification/notification';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import { api } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
import { notificationController, NotificationType } from '../shared-components/notification/notification';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
let error: string;
|
||||
let success: string;
|
||||
|
||||
let password = '';
|
||||
let confirmPassowrd = '';
|
||||
let password = '';
|
||||
let confirmPassowrd = '';
|
||||
|
||||
let canCreateUser = false;
|
||||
let canCreateUser = false;
|
||||
|
||||
let isCreatingUser = false;
|
||||
let isCreatingUser = false;
|
||||
|
||||
$: {
|
||||
if (password !== confirmPassowrd && confirmPassowrd.length > 0) {
|
||||
error = 'Password does not match';
|
||||
canCreateUser = false;
|
||||
} else {
|
||||
error = '';
|
||||
canCreateUser = true;
|
||||
}
|
||||
}
|
||||
const dispatch = createEventDispatcher();
|
||||
$: {
|
||||
if (password !== confirmPassowrd && confirmPassowrd.length > 0) {
|
||||
error = 'Password does not match';
|
||||
canCreateUser = false;
|
||||
} else {
|
||||
error = '';
|
||||
canCreateUser = true;
|
||||
}
|
||||
}
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
async function registerUser(event: SubmitEvent) {
|
||||
if (canCreateUser && !isCreatingUser) {
|
||||
isCreatingUser = true;
|
||||
async function registerUser(event: SubmitEvent) {
|
||||
if (canCreateUser && !isCreatingUser) {
|
||||
isCreatingUser = true;
|
||||
|
||||
error = '';
|
||||
error = '';
|
||||
|
||||
const formElement = event.target as HTMLFormElement;
|
||||
const formElement = event.target as HTMLFormElement;
|
||||
|
||||
const form = new FormData(formElement);
|
||||
const form = new FormData(formElement);
|
||||
|
||||
const email = form.get('email');
|
||||
const password = form.get('password');
|
||||
const firstName = form.get('firstName');
|
||||
const lastName = form.get('lastName');
|
||||
const email = form.get('email');
|
||||
const password = form.get('password');
|
||||
const firstName = form.get('firstName');
|
||||
const lastName = form.get('lastName');
|
||||
|
||||
try {
|
||||
const { status } = await api.userApi.createUser({
|
||||
createUserDto: {
|
||||
email: String(email),
|
||||
password: String(password),
|
||||
firstName: String(firstName),
|
||||
lastName: String(lastName)
|
||||
}
|
||||
});
|
||||
try {
|
||||
const { status } = await api.userApi.createUser({
|
||||
createUserDto: {
|
||||
email: String(email),
|
||||
password: String(password),
|
||||
firstName: String(firstName),
|
||||
lastName: String(lastName),
|
||||
},
|
||||
});
|
||||
|
||||
if (status === 201) {
|
||||
success = 'New user created';
|
||||
if (status === 201) {
|
||||
success = 'New user created';
|
||||
|
||||
dispatch('user-created');
|
||||
dispatch('user-created');
|
||||
|
||||
isCreatingUser = false;
|
||||
return;
|
||||
} else {
|
||||
error = 'Error create user account';
|
||||
isCreatingUser = false;
|
||||
}
|
||||
} catch (e) {
|
||||
error = 'Error create user account';
|
||||
isCreatingUser = false;
|
||||
isCreatingUser = false;
|
||||
return;
|
||||
} else {
|
||||
error = 'Error create user account';
|
||||
isCreatingUser = false;
|
||||
}
|
||||
} catch (e) {
|
||||
error = 'Error create user account';
|
||||
isCreatingUser = false;
|
||||
|
||||
console.log('[ERROR] registerUser', e);
|
||||
console.log('[ERROR] registerUser', e);
|
||||
|
||||
notificationController.show({
|
||||
message: `Error create new user, check console for more detail`,
|
||||
type: NotificationType.Error
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
notificationController.show({
|
||||
message: `Error create new user, check console for more detail`,
|
||||
type: NotificationType.Error,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</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"
|
||||
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">
|
||||
Create new user
|
||||
</h1>
|
||||
<p
|
||||
class="text-sm border rounded-md p-4 font-mono text-gray-600 dark:border-immich-dark-bg dark:text-gray-300"
|
||||
>
|
||||
Please provide your user with the password, they will have to change it on their first sign
|
||||
in.
|
||||
</p>
|
||||
</div>
|
||||
<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">Create new user</h1>
|
||||
<p class="text-sm border rounded-md p-4 font-mono text-gray-600 dark:border-immich-dark-bg dark:text-gray-300">
|
||||
Please provide your user with the password, they will have to change it on their first sign in.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form on:submit|preventDefault={registerUser} autocomplete="off">
|
||||
<div class="m-4 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" required />
|
||||
</div>
|
||||
<form on:submit|preventDefault={registerUser} autocomplete="off">
|
||||
<div class="m-4 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" required />
|
||||
</div>
|
||||
|
||||
<div class="m-4 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"
|
||||
required
|
||||
bind:value={password}
|
||||
/>
|
||||
</div>
|
||||
<div class="m-4 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" required bind:value={password} />
|
||||
</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>
|
||||
<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>
|
||||
|
||||
<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="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="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>
|
||||
<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 ml-4 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 p-4">
|
||||
<Button type="submit" disabled={isCreatingUser} fullwidth>Create</Button>
|
||||
</div>
|
||||
</form>
|
||||
{#if success}
|
||||
<p class="text-immich-primary ml-4 text-sm">{success}</p>
|
||||
{/if}
|
||||
<div class="flex w-full p-4">
|
||||
<Button type="submit" disabled={isCreatingUser} fullwidth>Create</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,187 +1,167 @@
|
|||
<script lang="ts">
|
||||
import { api, UserResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import AccountEditOutline from 'svelte-material-icons/AccountEditOutline.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType
|
||||
} from '../shared-components/notification/notification';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
|
||||
import { handleError } from '../../utils/handle-error';
|
||||
import { api, UserResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import AccountEditOutline from 'svelte-material-icons/AccountEditOutline.svelte';
|
||||
import { notificationController, NotificationType } from '../shared-components/notification/notification';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
|
||||
import { handleError } from '../../utils/handle-error';
|
||||
|
||||
export let user: UserResponseDto;
|
||||
export let canResetPassword = true;
|
||||
export let user: UserResponseDto;
|
||||
export let canResetPassword = true;
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
let error: string;
|
||||
let success: string;
|
||||
|
||||
let isShowResetPasswordConfirmation = false;
|
||||
let isShowResetPasswordConfirmation = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const editUser = async () => {
|
||||
try {
|
||||
const { id, email, firstName, lastName, storageLabel, externalPath } = user;
|
||||
const { status } = await api.userApi.updateUser({
|
||||
updateUserDto: {
|
||||
id,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
storageLabel: storageLabel || '',
|
||||
externalPath: externalPath || ''
|
||||
}
|
||||
});
|
||||
const editUser = async () => {
|
||||
try {
|
||||
const { id, email, firstName, lastName, storageLabel, externalPath } = user;
|
||||
const { status } = await api.userApi.updateUser({
|
||||
updateUserDto: {
|
||||
id,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
storageLabel: storageLabel || '',
|
||||
externalPath: externalPath || '',
|
||||
},
|
||||
});
|
||||
|
||||
if (status === 200) {
|
||||
dispatch('edit-success');
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to update user');
|
||||
}
|
||||
};
|
||||
if (status === 200) {
|
||||
dispatch('edit-success');
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to update user');
|
||||
}
|
||||
};
|
||||
|
||||
const resetPassword = async () => {
|
||||
try {
|
||||
const defaultPassword = 'password';
|
||||
const resetPassword = async () => {
|
||||
try {
|
||||
const defaultPassword = 'password';
|
||||
|
||||
const { status } = await api.userApi.updateUser({
|
||||
updateUserDto: {
|
||||
id: user.id,
|
||||
password: defaultPassword,
|
||||
shouldChangePassword: true
|
||||
}
|
||||
});
|
||||
const { status } = await api.userApi.updateUser({
|
||||
updateUserDto: {
|
||||
id: user.id,
|
||||
password: defaultPassword,
|
||||
shouldChangePassword: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (status == 200) {
|
||||
dispatch('reset-password-success');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error reseting user password', e);
|
||||
notificationController.show({
|
||||
message: 'Error reseting user password, check console for more details',
|
||||
type: NotificationType.Error
|
||||
});
|
||||
} finally {
|
||||
isShowResetPasswordConfirmation = false;
|
||||
}
|
||||
};
|
||||
if (status == 200) {
|
||||
dispatch('reset-password-success');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error reseting user password', e);
|
||||
notificationController.show({
|
||||
message: 'Error reseting user password, check console for more details',
|
||||
type: NotificationType.Error,
|
||||
});
|
||||
} finally {
|
||||
isShowResetPasswordConfirmation = false;
|
||||
}
|
||||
};
|
||||
</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"
|
||||
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 text-immich-primary dark:text-immich-dark-primary"
|
||||
>
|
||||
<AccountEditOutline size="4em" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
Edit user
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col place-items-center place-content-center gap-4 px-4 text-immich-primary dark:text-immich-dark-primary"
|
||||
>
|
||||
<AccountEditOutline size="4em" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">Edit user</h1>
|
||||
</div>
|
||||
|
||||
<form on:submit|preventDefault={editUser} autocomplete="off">
|
||||
<div class="m-4 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={user.email}
|
||||
/>
|
||||
</div>
|
||||
<form on:submit|preventDefault={editUser} autocomplete="off">
|
||||
<div class="m-4 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={user.email} />
|
||||
</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
|
||||
bind:value={user.firstName}
|
||||
/>
|
||||
</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
|
||||
bind:value={user.firstName}
|
||||
/>
|
||||
</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
|
||||
bind:value={user.lastName}
|
||||
/>
|
||||
</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 bind:value={user.lastName} />
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="storage-label">Storage Label</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="storage-label"
|
||||
name="storage-label"
|
||||
type="text"
|
||||
bind:value={user.storageLabel}
|
||||
/>
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="storage-label">Storage Label</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="storage-label"
|
||||
name="storage-label"
|
||||
type="text"
|
||||
bind:value={user.storageLabel}
|
||||
/>
|
||||
|
||||
<p>
|
||||
Note: To apply the Storage Label to previously uploaded assets, run the
|
||||
<a href="/admin/jobs-status" class="text-immich-primary dark:text-immich-dark-primary">
|
||||
Storage Migration Job</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
Note: To apply the Storage Label to previously uploaded assets, run the
|
||||
<a href="/admin/jobs-status" class="text-immich-primary dark:text-immich-dark-primary">
|
||||
Storage Migration Job</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="external-path">External Path</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="external-path"
|
||||
name="external-path"
|
||||
type="text"
|
||||
bind:value={user.externalPath}
|
||||
/>
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="external-path">External Path</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="external-path"
|
||||
name="external-path"
|
||||
type="text"
|
||||
bind:value={user.externalPath}
|
||||
/>
|
||||
|
||||
<p>
|
||||
Note: Absolute path of parent import directory. A user can only import files if they exist
|
||||
at or under this path.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
Note: Absolute path of parent import directory. A user can only import files if they exist at or under this
|
||||
path.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<p class="text-red-400 ml-4 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 px-4 gap-4 mt-8">
|
||||
{#if canResetPassword}
|
||||
<Button
|
||||
color="light-red"
|
||||
fullwidth
|
||||
on:click={() => (isShowResetPasswordConfirmation = true)}>Reset password</Button
|
||||
>
|
||||
{/if}
|
||||
<Button type="submit" fullwidth>Confirm</Button>
|
||||
</div>
|
||||
</form>
|
||||
{#if success}
|
||||
<p class="text-immich-primary ml-4 text-sm">{success}</p>
|
||||
{/if}
|
||||
<div class="flex w-full px-4 gap-4 mt-8">
|
||||
{#if canResetPassword}
|
||||
<Button color="light-red" fullwidth on:click={() => (isShowResetPasswordConfirmation = true)}
|
||||
>Reset password</Button
|
||||
>
|
||||
{/if}
|
||||
<Button type="submit" fullwidth>Confirm</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{#if isShowResetPasswordConfirmation}
|
||||
<ConfirmDialogue
|
||||
title="Reset Password"
|
||||
confirmText="Reset"
|
||||
on:confirm={resetPassword}
|
||||
on:cancel={() => (isShowResetPasswordConfirmation = false)}
|
||||
>
|
||||
<svelte:fragment slot="prompt">
|
||||
<p>
|
||||
Are you sure you want to reset <b>{user.firstName} {user.lastName}</b>'s password?
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
</ConfirmDialogue>
|
||||
<ConfirmDialogue
|
||||
title="Reset Password"
|
||||
confirmText="Reset"
|
||||
on:confirm={resetPassword}
|
||||
on:cancel={() => (isShowResetPasswordConfirmation = false)}
|
||||
>
|
||||
<svelte:fragment slot="prompt">
|
||||
<p>
|
||||
Are you sure you want to reset <b>{user.firstName} {user.lastName}</b>'s password?
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
</ConfirmDialogue>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,166 +1,166 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
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 Button from '../elements/buttons/button.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
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 Button from '../elements/buttons/button.svelte';
|
||||
|
||||
let error: string;
|
||||
let email = '';
|
||||
let password = '';
|
||||
let oauthError: string;
|
||||
export let authConfig: OAuthConfigResponseDto;
|
||||
let loading = false;
|
||||
let oauthLoading = true;
|
||||
let error: string;
|
||||
let email = '';
|
||||
let password = '';
|
||||
let oauthError: string;
|
||||
export let authConfig: OAuthConfigResponseDto;
|
||||
let loading = false;
|
||||
let oauthLoading = true;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
onMount(async () => {
|
||||
if (oauth.isCallback(window.location)) {
|
||||
try {
|
||||
await oauth.login(window.location);
|
||||
dispatch('success');
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error('Error [login-form] [oauth.callback]', e);
|
||||
oauthError = 'Unable to complete OAuth login';
|
||||
} finally {
|
||||
oauthLoading = false;
|
||||
}
|
||||
}
|
||||
onMount(async () => {
|
||||
if (oauth.isCallback(window.location)) {
|
||||
try {
|
||||
await oauth.login(window.location);
|
||||
dispatch('success');
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error('Error [login-form] [oauth.callback]', e);
|
||||
oauthError = 'Unable to complete OAuth login';
|
||||
} finally {
|
||||
oauthLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const { data } = await oauth.getConfig(window.location);
|
||||
authConfig = data;
|
||||
try {
|
||||
const { data } = await oauth.getConfig(window.location);
|
||||
authConfig = data;
|
||||
|
||||
const { enabled, url, autoLaunch } = authConfig;
|
||||
const { enabled, url, autoLaunch } = authConfig;
|
||||
|
||||
if (enabled && url && autoLaunch && !oauth.isAutoLaunchDisabled(window.location)) {
|
||||
await goto(`${AppRoute.AUTH_LOGIN}?autoLaunch=0`, { replaceState: true });
|
||||
await goto(url);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
authConfig.passwordLoginEnabled = true;
|
||||
handleError(error, 'Unable to connect!');
|
||||
}
|
||||
if (enabled && url && autoLaunch && !oauth.isAutoLaunchDisabled(window.location)) {
|
||||
await goto(`${AppRoute.AUTH_LOGIN}?autoLaunch=0`, { replaceState: true });
|
||||
await goto(url);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
authConfig.passwordLoginEnabled = true;
|
||||
handleError(error, 'Unable to connect!');
|
||||
}
|
||||
|
||||
oauthLoading = false;
|
||||
});
|
||||
oauthLoading = false;
|
||||
});
|
||||
|
||||
const login = async () => {
|
||||
try {
|
||||
error = '';
|
||||
loading = true;
|
||||
const login = async () => {
|
||||
try {
|
||||
error = '';
|
||||
loading = true;
|
||||
|
||||
const { data } = await api.authenticationApi.login({
|
||||
loginCredentialDto: {
|
||||
email,
|
||||
password
|
||||
}
|
||||
});
|
||||
const { data } = await api.authenticationApi.login({
|
||||
loginCredentialDto: {
|
||||
email,
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data.isAdmin && data.shouldChangePassword) {
|
||||
dispatch('first-login');
|
||||
return;
|
||||
}
|
||||
if (!data.isAdmin && data.shouldChangePassword) {
|
||||
dispatch('first-login');
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch('success');
|
||||
return;
|
||||
} catch (e) {
|
||||
error = 'Incorrect email or password';
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
};
|
||||
dispatch('success');
|
||||
return;
|
||||
} catch (e) {
|
||||
error = 'Incorrect email or password';
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
{#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}
|
||||
<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"
|
||||
autocomplete="email"
|
||||
bind:value={email}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<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="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" size="lg" fullwidth disabled={loading || oauthLoading}>
|
||||
{#if loading}
|
||||
<span class="h-6">
|
||||
<LoadingSpinner />
|
||||
</span>
|
||||
{:else}
|
||||
Login
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="my-5 flex w-full">
|
||||
<Button type="submit" size="lg" fullwidth disabled={loading || oauthLoading}>
|
||||
{#if loading}
|
||||
<span class="h-6">
|
||||
<LoadingSpinner />
|
||||
</span>
|
||||
{: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}
|
||||
size="lg"
|
||||
fullwidth
|
||||
color={authConfig.passwordLoginEnabled ? 'secondary' : 'primary'}
|
||||
>
|
||||
{#if oauthLoading}
|
||||
<span class="h-6">
|
||||
<LoadingSpinner />
|
||||
</span>
|
||||
{:else}
|
||||
{authConfig.buttonText || 'Login with OAuth'}
|
||||
{/if}
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
{#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}
|
||||
size="lg"
|
||||
fullwidth
|
||||
color={authConfig.passwordLoginEnabled ? 'secondary' : 'primary'}
|
||||
>
|
||||
{#if oauthLoading}
|
||||
<span class="h-6">
|
||||
<LoadingSpinner />
|
||||
</span>
|
||||
{: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>
|
||||
<p class="text-center dark:text-immich-dark-fg p-4">Login has been disabled.</p>
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue