ytptube/ui/app/composables/useDiagnostics.ts
2026-05-26 19:22:36 +03:00

100 lines
2.5 KiB
TypeScript

import { computed, readonly, ref } from 'vue';
import { useNotification } from '~/composables/useNotification';
import type { DiagnosticCheck, DiagnosticsResponse } from '~/types/diagnostics';
import { parse_api_error, parse_api_response, request } from '~/utils';
const diagnostics = ref<DiagnosticsResponse | null>(null);
const isLoading = ref(false);
const lastError = ref<string | null>(null);
const throwInstead = ref(false);
const groupedChecks = computed<Record<string, Array<DiagnosticCheck>>>(() => {
return (diagnostics.value?.checks ?? []).reduce<Record<string, Array<DiagnosticCheck>>>(
(acc, check) => {
if (!acc[check.group]) {
acc[check.group] = [];
}
acc[check.group]?.push(check);
return acc;
},
{},
);
});
const groupOrder = computed<Array<string>>(() => Object.keys(groupedChecks.value));
const readJson = async (response: Response): Promise<unknown> => {
try {
return await response.clone().json();
} catch {
return null;
}
};
const ensureSuccess = async (response: Response): Promise<void> => {
if (response.ok) {
return;
}
const payload = await readJson(response);
throw new Error(await parse_api_error(payload));
};
const handleError = (error: unknown): void => {
const message = error instanceof Error ? error.message : 'Failed to load diagnostics.';
lastError.value = message;
useNotification().error(message);
};
const loadDiagnostics = async (force: boolean = false): Promise<DiagnosticsResponse | null> => {
if (isLoading.value) {
return diagnostics.value;
}
if (diagnostics.value && !force) {
return diagnostics.value;
}
isLoading.value = true;
lastError.value = null;
try {
const response = await request(`/api/system/diagnostics${force ? '?refresh=1' : ''}`);
await ensureSuccess(response);
diagnostics.value = await parse_api_response<DiagnosticsResponse>(response.json());
return diagnostics.value;
} catch (error) {
handleError(error);
if (throwInstead.value) {
throw error;
}
return null;
} finally {
isLoading.value = false;
}
};
const clearError = (): void => {
lastError.value = null;
};
const __resetForTesting = (): void => {
diagnostics.value = null;
isLoading.value = false;
lastError.value = null;
throwInstead.value = false;
};
export const useDiagnostics = () => ({
diagnostics: readonly(diagnostics),
isLoading: readonly(isLoading),
lastError: readonly(lastError),
groupedChecks,
groupOrder,
loadDiagnostics,
clearError,
throwInstead,
__resetForTesting,
});