mirror of
https://github.com/immich-app/immich.git
synced 2025-01-07 00:50:23 -05:00
feat(web): add warning when setting a quota superior to the disk size (#6737)
* refactor: inline warning * fix: do not use onmount * chore: remove outdated comment * wording --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
4290a29107
commit
7a1f25b515
6 changed files with 41 additions and 27 deletions
|
@ -5,6 +5,7 @@
|
|||
import { notificationController, NotificationType } from '../shared-components/notification/notification';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import { convertToBytes } from '$lib/utils/byte-converter';
|
||||
import { serverInfo } from '$lib/stores/server-info.store';
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
|
@ -13,9 +14,11 @@
|
|||
let confirmPassowrd = '';
|
||||
|
||||
let canCreateUser = false;
|
||||
|
||||
let quotaSize: number | undefined = undefined;
|
||||
let isCreatingUser = false;
|
||||
|
||||
$: quotaSizeWarning = quotaSize && convertToBytes(Number(quotaSize), 'GiB') > $serverInfo.diskSizeRaw;
|
||||
|
||||
$: {
|
||||
if (password !== confirmPassowrd && confirmPassowrd.length > 0) {
|
||||
error = 'Password does not match';
|
||||
|
@ -121,8 +124,12 @@
|
|||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="quotaSize">Quota Size (GiB)</label>
|
||||
<input class="immich-form-input" id="quotaSize" name="quotaSize" type="number" min="0" />
|
||||
<label class="flex items-center gap-2 immich-form-label" for="quotaSize"
|
||||
>Quota Size (GiB) {#if quotaSizeWarning}
|
||||
<p class="text-red-400 text-sm">You set a quota higher than the disk size</p>
|
||||
{/if}</label
|
||||
>
|
||||
<input class="immich-form-input" id="quotaSize" name="quotaSize" type="number" min="0" bind:value={quotaSize} />
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
|
|
|
@ -10,14 +10,22 @@
|
|||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { convertFromBytes, convertToBytes } from '$lib/utils/byte-converter';
|
||||
import { serverInfo } from '$lib/stores/server-info.store';
|
||||
|
||||
export let user: UserResponseDto;
|
||||
export let canResetPassword = true;
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
|
||||
let isShowResetPasswordConfirmation = false;
|
||||
let quotaSize = user.quotaSizeInBytes ? convertFromBytes(user.quotaSizeInBytes, 'GiB') : null;
|
||||
|
||||
const previousQutoa = user.quotaSizeInBytes;
|
||||
|
||||
$: quotaSizeWarning =
|
||||
previousQutoa !== convertToBytes(Number(quotaSize), 'GiB') &&
|
||||
!!quotaSize &&
|
||||
convertToBytes(Number(quotaSize), 'GiB') > $serverInfo.diskSizeRaw;
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
close: void;
|
||||
|
@ -25,8 +33,6 @@
|
|||
editSuccess: void;
|
||||
}>();
|
||||
|
||||
let quotaSize = user.quotaSizeInBytes ? convertFromBytes(user.quotaSizeInBytes, 'GiB') : null;
|
||||
|
||||
const editUser = async () => {
|
||||
try {
|
||||
const { id, email, name, storageLabel, externalPath } = user;
|
||||
|
@ -102,7 +108,11 @@
|
|||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="quotaSize">Quota Size (GiB)</label>
|
||||
<label class="flex items-center gap-2 immich-form-label" for="quotaSize"
|
||||
>Quota Size (GiB) {#if quotaSizeWarning}
|
||||
<p class="text-red-400 text-sm">You set a quota higher than the disk size</p>
|
||||
{/if}</label
|
||||
>
|
||||
<input class="immich-form-input" id="quotaSize" name="quotaSize" type="number" min="0" bind:value={quotaSize} />
|
||||
<p>Note: Enter 0 for unlimited quota</p>
|
||||
</div>
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { websocketStore } from '$lib/stores/websocket';
|
||||
import { api } from '@api';
|
||||
import { onMount } from 'svelte';
|
||||
import { asByteUnitString } from '../../utils/byte-units';
|
||||
import LoadingSpinner from './loading-spinner.svelte';
|
||||
import { mdiChartPie, mdiDns } from '@mdi/js';
|
||||
import { serverInfoStore } from '$lib/stores/server-info.store';
|
||||
import { serverInfo } from '$lib/stores/server-info.store';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { requestServerInfo } from '$lib/utils/auth';
|
||||
|
||||
const { serverVersion, connected } = websocketStore;
|
||||
|
||||
|
@ -16,8 +16,8 @@
|
|||
|
||||
$: version = $serverVersion ? `v${$serverVersion.major}.${$serverVersion.minor}.${$serverVersion.patch}` : null;
|
||||
$: hasQuota = $user?.quotaSizeInBytes !== null;
|
||||
$: availableBytes = (hasQuota ? $user?.quotaSizeInBytes : $serverInfoStore?.diskSizeRaw) || 0;
|
||||
$: usedBytes = (hasQuota ? $user?.quotaUsageInBytes : $serverInfoStore?.diskUseRaw) || 0;
|
||||
$: availableBytes = (hasQuota ? $user?.quotaSizeInBytes : $serverInfo?.diskSizeRaw) || 0;
|
||||
$: usedBytes = (hasQuota ? $user?.quotaUsageInBytes : $serverInfo?.diskUseRaw) || 0;
|
||||
$: usedPercentage = Math.round((usedBytes / availableBytes) * 100);
|
||||
|
||||
const onUpdate = () => {
|
||||
|
@ -39,19 +39,8 @@
|
|||
$: $user && onUpdate();
|
||||
|
||||
onMount(async () => {
|
||||
await refresh();
|
||||
await requestServerInfo();
|
||||
});
|
||||
|
||||
const refresh = async () => {
|
||||
try {
|
||||
if (!$serverInfoStore) {
|
||||
const { data } = await api.serverInfoApi.getServerInfo();
|
||||
$serverInfoStore = data;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error [StatusBox] [onMount]');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="dark:text-immich-dark-fg">
|
||||
|
@ -64,7 +53,7 @@
|
|||
</div>
|
||||
<div class="hidden group-hover:sm:block md:block">
|
||||
<p class="text-sm font-medium text-immich-primary dark:text-immich-dark-primary">Storage</p>
|
||||
{#if $serverInfoStore}
|
||||
{#if $serverInfo}
|
||||
<div class="my-2 h-[7px] w-full rounded-full bg-gray-200 dark:bg-gray-700">
|
||||
<div class="h-[7px] rounded-full {usageClasses}" style="width: {usedPercentage}%" />
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { writable } from 'svelte/store';
|
||||
import type { ServerInfoResponseDto } from '@api';
|
||||
|
||||
export const serverInfoStore = writable<ServerInfoResponseDto>();
|
||||
export const serverInfo = writable<ServerInfoResponseDto>();
|
||||
|
|
|
@ -2,6 +2,7 @@ import { api } from '@api';
|
|||
import { redirect } from '@sveltejs/kit';
|
||||
import { AppRoute } from '../constants';
|
||||
import { getSavedUser, setUser } from '$lib/stores/user.store';
|
||||
import { serverInfo } from '$lib/stores/server-info.store';
|
||||
|
||||
export interface AuthOptions {
|
||||
admin?: true;
|
||||
|
@ -16,7 +17,6 @@ export const getAuthUser = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
// TODO: re-use already loaded user (once) instead of fetching on each page navigation
|
||||
export const authenticate = async (options?: AuthOptions) => {
|
||||
options = options || {};
|
||||
|
||||
|
@ -36,6 +36,13 @@ export const authenticate = async (options?: AuthOptions) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const requestServerInfo = async () => {
|
||||
if (getSavedUser()) {
|
||||
const { data } = await api.serverInfoApi.getServerInfo();
|
||||
serverInfo.set(data);
|
||||
}
|
||||
};
|
||||
|
||||
export const isLoggedIn = async () => {
|
||||
const savedUser = getSavedUser();
|
||||
const user = savedUser || (await getAuthUser());
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { authenticate } from '$lib/utils/auth';
|
||||
import { authenticate, requestServerInfo } from '$lib/utils/auth';
|
||||
import { api } from '@api';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await authenticate({ admin: true });
|
||||
await requestServerInfo();
|
||||
const { data: allUsers } = await api.userApi.getAllUsers({ isAll: false });
|
||||
|
||||
return {
|
||||
|
|
Loading…
Reference in a new issue