Update package dependencies and enhance session management features

- Added react-hot-toast for improved user notifications in the application.
- Updated baseline-browser-mapping to version 2.9.2.
- Introduced maxInstances property in Session and App schemas to manage concurrent instances.
- Refactored server session handling to enforce instance limits and improve error messaging.
- Removed GPU memory management from server registration and session assignment logic for cleaner implementation.
- Enhanced error handling in session management with localized messages for better user experience.
This commit is contained in:
2025-12-05 18:04:01 +05:00
parent 775ba52cd0
commit c384087f38
13 changed files with 234 additions and 254 deletions
+29 -5
View File
@@ -16,6 +16,7 @@
"motion": "^12.23.24",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-hot-toast": "^2.6.0",
"react-qr-code": "^2.0.18",
"react-router": "^7.9.3",
"socket.io-client": "^4.8.1",
@@ -30,6 +31,7 @@
"@types/uuid": "^11.0.0",
"@vitejs/plugin-react-swc": "^4.1.0",
"autoprefixer": "^10.4.21",
"baseline-browser-mapping": "^2.9.2",
"eslint": "^9.36.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.22",
@@ -1905,11 +1907,10 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.8.13",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.13.tgz",
"integrity": "sha512-7s16KR8io8nIBWQyCYhmFhd+ebIzb9VKTzki+wOJXHTxTnV6+mFGH3+Jwn1zoKaY9/H9T/0BcKCZnzXljPnpSQ==",
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.2.tgz",
"integrity": "sha512-PxSsosKQjI38iXkmb3d0Y32efqyA0uW4s41u4IVBsLlWLhCiYNpH/AfNOVWRqCQBlD8TFJTz6OUWNd4DFJCnmw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.js"
}
@@ -2168,7 +2169,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"devOptional": true,
"license": "MIT"
},
"node_modules/debug": {
@@ -2810,6 +2810,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/goober": {
"version": "2.1.18",
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.18.tgz",
"integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==",
"peerDependencies": {
"csstype": "^3.0.10"
}
},
"node_modules/graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@@ -3687,6 +3695,22 @@
"react": "^19.2.0"
}
},
"node_modules/react-hot-toast": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz",
"integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==",
"dependencies": {
"csstype": "^3.1.3",
"goober": "^2.1.16"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"react": ">=16",
"react-dom": ">=16"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+1
View File
@@ -33,6 +33,7 @@
"@types/uuid": "^11.0.0",
"@vitejs/plugin-react-swc": "^4.1.0",
"autoprefixer": "^10.4.21",
"baseline-browser-mapping": "^2.9.2",
"eslint": "^9.36.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.22",
+63
View File
@@ -0,0 +1,63 @@
import { HTTPError } from "ky";
/**
* Извлекает сообщение об ошибке из ответа API
* Elysia возвращает ошибки как строку в теле ответа или как JSON с полем message
*/
export async function extractErrorMessage(error: unknown): Promise<string> {
// Если это HTTPError от ky
if (error instanceof HTTPError) {
try {
// Сначала пытаемся получить текст ответа
const responseText = await error.response.text();
// Пытаемся распарсить как JSON
try {
const errorData = JSON.parse(responseText);
// Проверяем различные форматы ответа об ошибке
return (
errorData.message ||
errorData.error ||
responseText ||
error.response.statusText ||
`Ошибка ${error.response.status}`
);
} catch {
// Если не JSON, значит это строка - используем её напрямую
// Elysia возвращает ошибки как строку при использовании status(code, message)
return responseText || error.response.statusText || `Ошибка ${error.response.status}`;
}
} catch {
// Если не удалось получить текст ответа, используем статус код
const statusMessages: Record<number, string> = {
400: "Неверный запрос",
401: "Требуется авторизация",
403: "Доступ запрещен",
404: "Ресурс не найден",
409: "Конфликт данных",
500: "Внутренняя ошибка сервера",
503: "Сервис временно недоступен",
};
return (
statusMessages[error.response.status] ||
error.response.statusText ||
`Ошибка ${error.response.status}`
);
}
}
// Если это обычная Error
if (error instanceof Error) {
return error.message;
}
// Если это объект с полем message
if (typeof error === "object" && error !== null && "message" in error) {
return String((error as { message: unknown }).message);
}
// Дефолтное сообщение
return "Произошла неизвестная ошибка";
}
+14 -3
View File
@@ -1,6 +1,8 @@
import { useState } from "react";
import { api } from "../lib/api";
import { useNavigate } from "react-router";
import toast from "react-hot-toast";
import { extractErrorMessage } from "../lib/errorUtils";
interface Session {
id: string;
@@ -44,9 +46,18 @@ function TestPage() {
navigate(`/sessions/${response.session.id}`);
} catch (err) {
console.error("Failed to start session:", err);
setError(
err instanceof Error ? err.message : "Не удалось запустить приложение"
);
// Извлекаем сообщение об ошибке из ответа
const errorMessage = await extractErrorMessage(err);
// Показываем ошибку через toast
toast.error(errorMessage, {
duration: 5000,
position: "top-center",
});
// Также сохраняем для отображения в UI (если нужно)
setError(errorMessage);
} finally {
setIsLoading(false);
}
+1
View File
@@ -22,6 +22,7 @@ export interface Session {
title: string;
gpuLimitMb: number | null;
psVersion: number | null;
maxInstances: number | null;
};
server?: {
id: string;