mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
chore(web) Add automatic server stats refetching (#1271)
This commit is contained in:
parent
af2eac52a8
commit
5999af6c78
@ -19,7 +19,6 @@
|
|||||||
allJobsStatus = data;
|
allJobsStatus = data;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
1;
|
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
clearInterval(setIntervalHandler);
|
clearInterval(setIntervalHandler);
|
||||||
|
@ -1,13 +1,32 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ServerStatsResponseDto, UserResponseDto } from '@api';
|
import { api, ServerStatsResponseDto, UserResponseDto } from '@api';
|
||||||
import CameraIris from 'svelte-material-icons/CameraIris.svelte';
|
import CameraIris from 'svelte-material-icons/CameraIris.svelte';
|
||||||
import PlayCircle from 'svelte-material-icons/PlayCircle.svelte';
|
import PlayCircle from 'svelte-material-icons/PlayCircle.svelte';
|
||||||
import Memory from 'svelte-material-icons/Memory.svelte';
|
import Memory from 'svelte-material-icons/Memory.svelte';
|
||||||
import StatsCard from './stats-card.svelte';
|
import StatsCard from './stats-card.svelte';
|
||||||
import { getBytesWithUnit, asByteUnitString } from '../../../utils/byte-units';
|
import { getBytesWithUnit, asByteUnitString } from '../../../utils/byte-units';
|
||||||
export let stats: ServerStatsResponseDto;
|
import { onMount, onDestroy } from 'svelte';
|
||||||
|
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||||
|
|
||||||
export let allUsers: Array<UserResponseDto>;
|
export let allUsers: Array<UserResponseDto>;
|
||||||
|
|
||||||
|
let stats: ServerStatsResponseDto;
|
||||||
|
let setIntervalHandler: NodeJS.Timer;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const { data } = await api.serverInfoApi.getStats();
|
||||||
|
stats = data;
|
||||||
|
|
||||||
|
setIntervalHandler = setInterval(async () => {
|
||||||
|
const { data } = await api.serverInfoApi.getStats();
|
||||||
|
stats = data;
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
clearInterval(setIntervalHandler);
|
||||||
|
});
|
||||||
|
|
||||||
const getFullName = (userId: string) => {
|
const getFullName = (userId: string) => {
|
||||||
let name = 'Admin'; // since we do not have admin user in allUsers
|
let name = 'Admin'; // since we do not have admin user in allUsers
|
||||||
allUsers.forEach((user) => {
|
allUsers.forEach((user) => {
|
||||||
@ -16,7 +35,8 @@
|
|||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
$: [spaceUsage, spaceUnit] = getBytesWithUnit(stats.usageRaw);
|
// Stats are unavailable if data is not loaded yet
|
||||||
|
$: [spaceUsage, spaceUnit] = getBytesWithUnit(stats ? stats.usageRaw : 0);
|
||||||
|
|
||||||
const locale = navigator.language;
|
const locale = navigator.language;
|
||||||
</script>
|
</script>
|
||||||
@ -26,9 +46,14 @@
|
|||||||
<p class="text-sm dark:text-immich-dark-fg">TOTAL USAGE</p>
|
<p class="text-sm dark:text-immich-dark-fg">TOTAL USAGE</p>
|
||||||
|
|
||||||
<div class="flex mt-5 justify-between">
|
<div class="flex mt-5 justify-between">
|
||||||
<StatsCard logo={CameraIris} title={'PHOTOS'} value={stats.photos.toString()} />
|
<StatsCard logo={CameraIris} title={'PHOTOS'} value={stats && stats.photos.toString()} />
|
||||||
<StatsCard logo={PlayCircle} title={'VIDEOS'} value={stats.videos.toString()} />
|
<StatsCard logo={PlayCircle} title={'VIDEOS'} value={stats && stats.videos.toString()} />
|
||||||
<StatsCard logo={Memory} title={'STORAGE'} value={spaceUsage.toString()} unit={spaceUnit} />
|
<StatsCard
|
||||||
|
logo={Memory}
|
||||||
|
title={'STORAGE'}
|
||||||
|
value={stats && spaceUsage.toString()}
|
||||||
|
unit={spaceUnit}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -48,20 +73,30 @@
|
|||||||
<tbody
|
<tbody
|
||||||
class="overflow-y-auto rounded-md w-full max-h-[320px] block border dark:border-immich-dark-gray dark:text-immich-dark-fg"
|
class="overflow-y-auto rounded-md w-full max-h-[320px] block border dark:border-immich-dark-gray dark:text-immich-dark-fg"
|
||||||
>
|
>
|
||||||
{#each stats.usageByUser as user, i}
|
{#if stats}
|
||||||
|
{#each stats.usageByUser as user, i}
|
||||||
|
<tr
|
||||||
|
class={`text-center flex place-items-center w-full h-[50px] ${
|
||||||
|
i % 2 == 0
|
||||||
|
? 'bg-immich-gray dark:bg-immich-dark-gray/75'
|
||||||
|
: 'bg-immich-bg dark:bg-immich-dark-gray/50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<td class="text-sm px-2 w-1/4 text-ellipsis">{getFullName(user.userId)}</td>
|
||||||
|
<td class="text-sm px-2 w-1/4 text-ellipsis">{user.photos.toLocaleString(locale)}</td>
|
||||||
|
<td class="text-sm px-2 w-1/4 text-ellipsis">{user.videos.toLocaleString(locale)}</td>
|
||||||
|
<td class="text-sm px-2 w-1/4 text-ellipsis">{asByteUnitString(user.usageRaw)}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
<tr
|
<tr
|
||||||
class={`text-center flex place-items-center w-full h-[50px] ${
|
class="text-center flex place-items-center w-full h-[50px] bg-immich-gray dark:bg-immich-dark-gray/75"
|
||||||
i % 2 == 0
|
|
||||||
? 'bg-immich-gray dark:bg-immich-dark-gray/75'
|
|
||||||
: 'bg-immich-bg dark:bg-immich-dark-gray/50'
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<td class="text-sm px-2 w-1/4 text-ellipsis">{getFullName(user.userId)}</td>
|
<td class="w-full flex justify-center">
|
||||||
<td class="text-sm px-2 w-1/4 text-ellipsis">{user.photos.toLocaleString(locale)}</td>
|
<LoadingSpinner />
|
||||||
<td class="text-sm px-2 w-1/4 text-ellipsis">{user.videos.toLocaleString(locale)}</td>
|
</td>
|
||||||
<td class="text-sm px-2 w-1/4 text-ellipsis">{asByteUnitString(user.usageRaw)}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/if}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export let logo: any;
|
export let logo: any;
|
||||||
export let title: string;
|
export let title: string;
|
||||||
@ -6,8 +8,12 @@
|
|||||||
export let unit: string | undefined = undefined;
|
export let unit: string | undefined = undefined;
|
||||||
|
|
||||||
$: zeros = () => {
|
$: zeros = () => {
|
||||||
let result = '';
|
if (!value) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
const maxLength = 13;
|
const maxLength = 13;
|
||||||
|
let result = '';
|
||||||
const valueLength = parseInt(value).toString().length;
|
const valueLength = parseInt(value).toString().length;
|
||||||
const zeroLength = maxLength - valueLength;
|
const zeroLength = maxLength - valueLength;
|
||||||
for (let i = 0; i < zeroLength; i++) {
|
for (let i = 0; i < zeroLength; i++) {
|
||||||
@ -26,9 +32,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="relative text-center font-mono font-semibold text-2xl">
|
<div class="relative text-center font-mono font-semibold text-2xl">
|
||||||
<span class="text-[#DCDADA] dark:text-[#525252]">{zeros()}</span><span
|
{#if value !== undefined}
|
||||||
class="text-immich-primary dark:text-immich-dark-primary">{parseInt(value)}</span
|
<span class="text-[#DCDADA] dark:text-[#525252]">{zeros()}</span><span
|
||||||
>
|
class="text-immich-primary dark:text-immich-dark-primary">{parseInt(value)}</span
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<div class="flex justify-end pr-2">
|
||||||
|
<LoadingSpinner />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{#if unit}
|
{#if unit}
|
||||||
<span class="absolute -top-5 right-2 text-base font-light text-gray-400">{unit}</span>
|
<span class="absolute -top-5 right-2 text-base font-light text-gray-400">{unit}</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -13,5 +13,5 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
|
|
||||||
const { data: allUsers } = await serverApi.userApi.getAllUsers(false);
|
const { data: allUsers } = await serverApi.userApi.getAllUsers(false);
|
||||||
|
|
||||||
return { user, allUsers };
|
return { allUsers };
|
||||||
};
|
};
|
||||||
|
@ -1,29 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ServerStatsPanel from '$lib/components/admin-page/server-stats/server-stats-panel.svelte';
|
import ServerStatsPanel from '$lib/components/admin-page/server-stats/server-stats-panel.svelte';
|
||||||
import { api, ServerStatsResponseDto } from '@api';
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
let serverStat: ServerStatsResponseDto;
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
getServerStats();
|
|
||||||
});
|
|
||||||
|
|
||||||
const getServerStats = async () => {
|
|
||||||
try {
|
|
||||||
const res = await api.serverInfoApi.getStats();
|
|
||||||
serverStat = res.data;
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Server Status - Immich</title>
|
<title>Server Status - Immich</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
{#if $page.data.allUsers && serverStat}
|
{#if $page.data.allUsers}
|
||||||
<ServerStatsPanel stats={serverStat} allUsers={$page.data.allUsers} />
|
<ServerStatsPanel allUsers={$page.data.allUsers} />
|
||||||
{/if}
|
{/if}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user