From 8e37ea869aee119c56e858f1be6d1a09572a5d96 Mon Sep 17 00:00:00 2001 From: Richard Haddad Date: Mon, 24 Aug 2026 18:41:27 +0200 Subject: [PATCH 1/2] fix: better error logging --- .../ExceptionHandlingMiddleware.cs | 4 +- .../backup/services/BackupService.cs | 2 +- PKVault.Backend/db/loader/PkmFileLoader.cs | 5 +- .../db/loader/save/SavesLoadersService.cs | 2 +- PKVault.Backend/db/services/SessionService.cs | 2 +- .../settings/services/SettingsService.cs | 4 +- .../storage/services/ActionService.cs | 2 +- .../storage/services/PkmLegalityService.cs | 3 +- PKVault.Desktop/Program.cs | 83 +++++++++++-------- frontend/package-lock.json | 28 +++++++ frontend/package.json | 1 + frontend/vite.config.ts | 2 + 12 files changed, 91 insertions(+), 47 deletions(-) diff --git a/PKVault.Backend/ExceptionHandlingMiddleware.cs b/PKVault.Backend/ExceptionHandlingMiddleware.cs index d31148cdf..887f0083b 100644 --- a/PKVault.Backend/ExceptionHandlingMiddleware.cs +++ b/PKVault.Backend/ExceptionHandlingMiddleware.cs @@ -17,9 +17,9 @@ public async Task Invoke(HttpContext context) } } - private static async Task WriteExceptionResponse(HttpContext context, Exception ex) + public static async Task WriteExceptionResponse(HttpContext context, Exception ex) { - Log.Error(ex.ToString()); + Log.Error(ex, "Exception during web request"); var response = context.Response; if (response.HasStarted) { diff --git a/PKVault.Backend/backup/services/BackupService.cs b/PKVault.Backend/backup/services/BackupService.cs index b47fa4691..d28d50ec1 100644 --- a/PKVault.Backend/backup/services/BackupService.cs +++ b/PKVault.Backend/backup/services/BackupService.cs @@ -414,7 +414,7 @@ public async Task PrepareBackupThenRun(string backupName, DataUpdateFlags flags, } catch (Exception ex) { - log.LogError(ex.ToString()); + log.LogError(ex, "Exception during action run"); await RestoreBackup(bkpDateTime, withSafeBackup: false, flags); diff --git a/PKVault.Backend/db/loader/PkmFileLoader.cs b/PKVault.Backend/db/loader/PkmFileLoader.cs index 51ba28b6b..9c8cce3f1 100644 --- a/PKVault.Backend/db/loader/PkmFileLoader.cs +++ b/PKVault.Backend/db/loader/PkmFileLoader.cs @@ -57,7 +57,7 @@ public static async Task LoadPkmFile(IFileIOService fileIOService } catch (Exception ex) { - Log.Warning(ex.ToString()); + Log.Warning(ex, "Exception during PKM file load"); pkmFile.Data = []; pkmFile.Error = GetPKMLoadError(ex); @@ -216,8 +216,7 @@ public ImmutablePKM CreatePKM(PkmFileEntity entity, EntityContext context) } catch (Exception ex) { - Log.Error($"PKM file load failure with PkmFileEntity.Filepath=${filepath}"); - Log.Error(ex.ToString()); + Log.Error(ex, $"PKM file load failure with PkmFileEntity.Filepath=${filepath}"); pkm = GetPlaceholderPKM(); loadError = GetPKMLoadError(ex); diff --git a/PKVault.Backend/db/loader/save/SavesLoadersService.cs b/PKVault.Backend/db/loader/save/SavesLoadersService.cs index 81b0c21bf..a38323fb5 100644 --- a/PKVault.Backend/db/loader/save/SavesLoadersService.cs +++ b/PKVault.Backend/db/loader/save/SavesLoadersService.cs @@ -213,7 +213,7 @@ private async Task> ReadSaveFiles() } catch (Exception ex) { - log.LogError(ex.ToString()); + log.LogError(ex, $"Exception during save load, path={path}"); return null; } } diff --git a/PKVault.Backend/db/services/SessionService.cs b/PKVault.Backend/db/services/SessionService.cs index 7e049a8a0..261051b1a 100644 --- a/PKVault.Backend/db/services/SessionService.cs +++ b/PKVault.Backend/db/services/SessionService.cs @@ -130,7 +130,7 @@ private async Task CheckDataToNormalize(IServiceScope scope, DataUpdateFla } catch (Exception ex) { - log.LogError(ex.ToString()); + log.LogError(ex, "Exception during external-pkms check/update"); } return false; } diff --git a/PKVault.Backend/settings/services/SettingsService.cs b/PKVault.Backend/settings/services/SettingsService.cs index 1d98f24f5..fe498525b 100644 --- a/PKVault.Backend/settings/services/SettingsService.cs +++ b/PKVault.Backend/settings/services/SettingsService.cs @@ -314,7 +314,7 @@ static bool IsSteamDeck() } catch (Exception ex) { - Log.Error(ex.ToString()); + Log.Error(ex, "Exception during is-steamdeck check"); } try @@ -325,7 +325,7 @@ static bool IsSteamDeck() } catch (Exception ex) { - Log.Error(ex.ToString()); + Log.Error(ex, "Exception during is-steamdeck hostname check"); } return false; diff --git a/PKVault.Backend/storage/services/ActionService.cs b/PKVault.Backend/storage/services/ActionService.cs index 1181da049..e6b92910e 100644 --- a/PKVault.Backend/storage/services/ActionService.cs +++ b/PKVault.Backend/storage/services/ActionService.cs @@ -323,7 +323,7 @@ await scope.ServiceProvider.GetRequiredService() } catch (Exception ex) { - log.LogError(ex.ToString()); + log.LogError(ex, "Exception during action add"); await RemoveDataActionsAndReset(sessionService.Actions.Count); diff --git a/PKVault.Backend/storage/services/PkmLegalityService.cs b/PKVault.Backend/storage/services/PkmLegalityService.cs index 7d1941a92..5cede7b57 100644 --- a/PKVault.Backend/storage/services/PkmLegalityService.cs +++ b/PKVault.Backend/storage/services/PkmLegalityService.cs @@ -44,8 +44,7 @@ private PkmLegalityDTO CreateDTO( } catch (Exception ex) { - Log.Error($"ValidityReport exception, id={id}"); - Log.Error(ex.ToString()); + Log.Error(ex, $"Exception during ValidityReport, id={id}"); ValidityReport = ex.ToString(); } diff --git a/PKVault.Desktop/Program.cs b/PKVault.Desktop/Program.cs index 989900073..969df711e 100644 --- a/PKVault.Desktop/Program.cs +++ b/PKVault.Desktop/Program.cs @@ -81,8 +81,16 @@ static void Main(string[] args) { Log.Logger.Debug("CREATED"); - var backendServerPostRun = await SetupBackendServer(server, args); - await backendServerPostRun(); + try + { + var backendServerPostRun = await SetupBackendServer(server, args); + await backendServerPostRun(); + } + catch (Exception ex) + { + Log.Fatal(ex, "An unhandled exception occurred post window created"); + throw; + } }); window.RegisterWindowClosingHandler((sender, e) => @@ -140,40 +148,47 @@ private static Func SetupStaticAssetsServer(out string baseUrl) server.Map("{**catchAll}", async context => { - // log.LogInformation("GET => " + context.Request.Path.Value); - // log.LogInformation(context.Request.GetDisplayUrl()); - // log.LogInformation(context.Request.GetEncodedUrl()); - - // http://localhost:8000/api/storage/main/pkm-version - // http://localhost:8000/index.html?server=http://localhost:57471 - var uri = context.Request.GetDisplayUrl(); - // log.LogInformation($"DEBUG {uri}"); - - var uriParts = uri.Split('?')[0].Split('/'); - - var uriActionAndRest = uriParts.Skip(3); - var uriAction = uriActionAndRest.First(); - var uriDirectories = uriActionAndRest.SkipLast(1); - var uriFilename = uriActionAndRest.Last(); - var uriFilenameExt = Path.GetExtension(uriFilename); - var assemblyActionAndRest = string.Join('.', [ - ..uriDirectories.Select(part => part.Replace('-', '_')), - uriFilename - ]); - - var streamKey = $"{AssemblyStaticPrefix}{assemblyActionAndRest}"; - var stream = Assembly.GetManifestResourceStream(streamKey); - if (stream == null) + try { - Log.Error($"Stream not found for key {streamKey}"); - // args.Response = webView.CoreWebView2.Environment.CreateWebResourceResponse(stream, 404, "Not Found", ""); - return; - } + // log.LogInformation("GET => " + context.Request.Path.Value); + // log.LogInformation(context.Request.GetDisplayUrl()); + // log.LogInformation(context.Request.GetEncodedUrl()); - contentTypeProvider.Mappings.TryGetValue(uriFilenameExt, out var contentType); + // http://localhost:8000/api/storage/main/pkm-version + // http://localhost:8000/index.html?server=http://localhost:57471 + var uri = context.Request.GetDisplayUrl(); + // log.LogInformation($"DEBUG {uri}"); - context.Response.ContentType = contentType; - await stream.CopyToAsync(context.Response.Body); + if (uri.EndsWith("/.well-known/appspecific/com.chrome.devtools.json")) + { + context.Response.StatusCode = Microsoft.AspNetCore.Http.StatusCodes.Status404NotFound; + return; + } + + var uriParts = uri.Split('?')[0].Split('/'); + + var uriActionAndRest = uriParts.Skip(3); + var uriAction = uriActionAndRest.First(); + var uriDirectories = uriActionAndRest.SkipLast(1); + var uriFilename = uriActionAndRest.Last(); + var uriFilenameExt = Path.GetExtension(uriFilename); + var assemblyActionAndRest = string.Join('.', [ + ..uriDirectories.Select(part => part.Replace('-', '_')), + uriFilename + ]); + + var streamKey = $"{AssemblyStaticPrefix}{assemblyActionAndRest}"; + var stream = Assembly.GetManifestResourceStream(streamKey) + ?? throw new ArgumentException($"Stream not found for key {streamKey}, uri {uri}"); + contentTypeProvider.Mappings.TryGetValue(uriFilenameExt, out var contentType); + + context.Response.ContentType = contentType; + await stream.CopyToAsync(context.Response.Body); + } + catch (Exception ex) + { + await ExceptionHandlingMiddleware.WriteExceptionResponse(context, ex); + } }); return () => server.RunAsync(); @@ -383,7 +398,7 @@ async Task GetDialogResponse() } catch (JsonException ex) { - Log.Error(ex.ToString()); + Log.Error(ex, "JsonException during frontend message recept"); } }); } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 707578397..78c2c5204 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -82,6 +82,7 @@ "typescript-eslint": "^8.59.0", "vite": "^8.0.3", "vite-css-modules": "^1.16.0", + "vite-plugin-devtools-json": "^1.1.0", "vite-plugin-image-optimizer": "^2.0.3", "vite-plugin-svgr": "^5.2.0", "vitest": "^4.1.8" @@ -15965,6 +15966,20 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.2.tgz", + "integrity": "sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -16125,6 +16140,19 @@ } } }, + "node_modules/vite-plugin-devtools-json": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-devtools-json/-/vite-plugin-devtools-json-1.1.0.tgz", + "integrity": "sha512-Fy9nMgMudGzDtUh1tGL7v0WAH0gsnZcZu2LFr/+1YxvYaJnxgbqRqv4jLU2tn5L9oqSaETHVCdDo9f8mSCQEZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uuid": "^14.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/vite-plugin-image-optimizer": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/vite-plugin-image-optimizer/-/vite-plugin-image-optimizer-2.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index be4dc8360..ba3ec2d34 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -100,6 +100,7 @@ "typescript-eslint": "^8.59.0", "vite": "^8.0.3", "vite-css-modules": "^1.16.0", + "vite-plugin-devtools-json": "^1.1.0", "vite-plugin-image-optimizer": "^2.0.3", "vite-plugin-svgr": "^5.2.0", "vitest": "^4.1.8" diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 559436db7..616da843f 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -5,6 +5,7 @@ import { tanstackRouter } from "@tanstack/router-plugin/vite"; import react, { reactCompilerPreset } from '@vitejs/plugin-react'; import { defineConfig } from "vite"; import { patchCssModules } from 'vite-css-modules'; +import devtoolsJson from 'vite-plugin-devtools-json'; import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'; import svgr from "vite-plugin-svgr"; import { prepareDocs } from './src/help/prepare-docs'; @@ -25,6 +26,7 @@ export default defineConfig({ generateSourceTypes: true, declarationMap: true }), + devtoolsJson(), tanstackRouter({ target: "react", autoCodeSplitting: true, From 8542f8a7609a87ab93238bcbca8edce9c1692bfa Mon Sep 17 00:00:00 2001 From: Richard Haddad Date: Mon, 24 Aug 2026 18:41:50 +0200 Subject: [PATCH 2/2] fix: app crash when github fetch returns errors --- .../notification/hooks/use-check-update.ts | 6 ++-- .../src/notification/notification-button.tsx | 5 ++-- .../settings/about/settings-about-right.tsx | 28 +++++++++++-------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/frontend/src/notification/hooks/use-check-update.ts b/frontend/src/notification/hooks/use-check-update.ts index d97cde87d..ef051b71d 100644 --- a/frontend/src/notification/hooks/use-check-update.ts +++ b/frontend/src/notification/hooks/use-check-update.ts @@ -29,14 +29,14 @@ export const useCheckUpdate = (): string | undefined => { const updateQuery = useQuery({ queryKey: [ 'check-update' ], queryFn: () => fetch('https://api.github.com/repos/chnapy/PKVault/releases/latest') - .then<{ + .then(res => res.json()), + }> | undefined>(res => res.json()), }); - if (!updateQuery.data || !settingsQuery.data) { + if (!updateQuery.data?.name || !settingsQuery.data) { return; } diff --git a/frontend/src/notification/notification-button.tsx b/frontend/src/notification/notification-button.tsx index f465e89f4..0f75a8d5e 100644 --- a/frontend/src/notification/notification-button.tsx +++ b/frontend/src/notification/notification-button.tsx @@ -3,6 +3,7 @@ import { BellIcon } from 'lucide-react'; import React from 'react'; import { BackendErrorsContext } from '../data/backend-errors-context'; import { useWarningsGetWarnings } from '../data/sdk/warnings/warnings.gen'; +import { withErrorCatcher } from '../error/with-error-catcher'; import { useTranslate } from '../translate/i18n'; import { UIActionIcon } from '../ui/form/button/ui-action-icon'; import type { PopoverContext } from '../ui/interaction/focus-controls/components/popover/context/popover-context'; @@ -40,7 +41,7 @@ const useOpened = () => { }; }; -export const NotificationButton: React.FC = () => { +export const NotificationButton: React.FC = withErrorCatcher('item', () => { const { t } = useTranslate(); const { hasAlerts, opened, setOpened } = useOpened(); @@ -75,4 +76,4 @@ export const NotificationButton: React.FC = () => { }} /> ); -}; +}); diff --git a/frontend/src/settings/about/settings-about-right.tsx b/frontend/src/settings/about/settings-about-right.tsx index 674dafafb..3be45d060 100644 --- a/frontend/src/settings/about/settings-about-right.tsx +++ b/frontend/src/settings/about/settings-about-right.tsx @@ -21,20 +21,22 @@ export const SettingsAboutRight: React.FC = () => { const releasesQuery = useQuery({ queryKey: [ 'release-list' ], queryFn: () => fetch('https://api.github.com/repos/chnapy/PKVault/releases') - .then<{ - url: string; + .then(res => res.json()) - .then(data => data.sort((r1, r2) => { - const state = getReleaseVersionState(r1.name.substring(1), r2.name.substring(1)); + }>[] | undefined>(res => res.json()) + .then(data => (data ?? []).sort((r1, r2) => { + const state = r1.name && r2.name + ? getReleaseVersionState(r1.name.substring(1), r2.name.substring(1)) + : 'same'; return switchUtil(state, { new: -1, old: 1, @@ -61,7 +63,9 @@ export const SettingsAboutRight: React.FC = () => { {isPending && } {!isPending && releasesQuery.data?.map(r => { - const releaseState = getReleaseVersionState(r.name.substring(1), settingsVersion ?? ''); + const releaseState = r.name + ? getReleaseVersionState(r.name.substring(1), settingsVersion ?? '') + : 'same'; return { {r.name} - {renderDate(new Date(r.published_at))} + {r.published_at && renderDate(new Date(r.published_at))} {releaseState === 'same' && @@ -105,7 +109,7 @@ export const SettingsAboutRight: React.FC = () => { - {r.body + {(r.body ?? '') .replaceAll(/@(\w+)/g, (match, name) => { return `[@${name}](https://github.com/${name})`; })