Add frontend boot diagnostics

This commit is contained in:
Dymas
2026-06-22 10:13:31 +02:00
parent 1fd3eff5b3
commit e5d09fe02b
4 changed files with 50 additions and 8 deletions
+25 -7
View File
@@ -53,6 +53,7 @@ export interface SpootyWorkspaceTabsState {
const WORKSPACE_STORAGE_KEY = 'jarri_spooty_workspace_state_v1'; const WORKSPACE_STORAGE_KEY = 'jarri_spooty_workspace_state_v1';
const WORKSPACE_TABS_STORAGE_KEY = 'jarri_spooty_workspace_tabs_v1'; const WORKSPACE_TABS_STORAGE_KEY = 'jarri_spooty_workspace_tabs_v1';
const ARCHIVE_DESTINATION_KEY = 'jarri_spooty_archive_destination_v1'; const ARCHIVE_DESTINATION_KEY = 'jarri_spooty_archive_destination_v1';
const AUTH_TOKEN_STORAGE_KEY = 'spooty_auth_token';
const GRID_SIZE = 12; const GRID_SIZE = 12;
const MIN_PANEL_WIDTH = 260; const MIN_PANEL_WIDTH = 260;
const MIN_PANEL_HEIGHT = 160; const MIN_PANEL_HEIGHT = 160;
@@ -176,9 +177,26 @@ export function defaultWorkspaceTabsState(): SpootyWorkspaceTabsState {
}; };
} }
function getStorageItem(key: string): string | null {
try {
return localStorage.getItem(key);
} catch (error) {
console.error(`Failed to read localStorage key "${key}"`, error);
return null;
}
}
function setStorageItem(key: string, value: string): void {
try {
localStorage.setItem(key, value);
} catch (error) {
console.error(`Failed to write localStorage key "${key}"`, error);
}
}
export function loadWorkspaceState(): SpootyWorkspaceState { export function loadWorkspaceState(): SpootyWorkspaceState {
try { try {
const raw = localStorage.getItem(WORKSPACE_STORAGE_KEY); const raw = getStorageItem(WORKSPACE_STORAGE_KEY);
if (!raw) { if (!raw) {
return defaultWorkspaceState(); return defaultWorkspaceState();
@@ -204,7 +222,7 @@ export function loadWorkspaceState(): SpootyWorkspaceState {
export function loadWorkspaceTabsState(): SpootyWorkspaceTabsState { export function loadWorkspaceTabsState(): SpootyWorkspaceTabsState {
try { try {
const raw = localStorage.getItem(WORKSPACE_TABS_STORAGE_KEY); const raw = getStorageItem(WORKSPACE_TABS_STORAGE_KEY);
if (raw) { if (raw) {
const parsed = JSON.parse(raw) as SpootyWorkspaceTabsState; const parsed = JSON.parse(raw) as SpootyWorkspaceTabsState;
@@ -318,8 +336,8 @@ export class AppComponent implements OnDestroy {
selectedTrack$ = this.trackService.selectedTrack$; selectedTrack$ = this.trackService.selectedTrack$;
version = this.versionService.getVersion(); version = this.versionService.getVersion();
spotifyConnected = false; spotifyConnected = false;
authTokenPresent = Boolean(localStorage.getItem('spooty_auth_token')); authTokenPresent = Boolean(getStorageItem(AUTH_TOKEN_STORAGE_KEY));
archiveDestination = localStorage.getItem(ARCHIVE_DESTINATION_KEY) || ''; archiveDestination = getStorageItem(ARCHIVE_DESTINATION_KEY) || '';
archiveListing?: ArchiveListing; archiveListing?: ArchiveListing;
archiveLoading = false; archiveLoading = false;
archiveError = ''; archiveError = '';
@@ -370,7 +388,7 @@ export class AppComponent implements OnDestroy {
return; return;
} }
localStorage.setItem('spooty_auth_token', token); setStorageItem(AUTH_TOKEN_STORAGE_KEY, token);
this.authTokenPresent = true; this.authTokenPresent = true;
url.searchParams.delete('token'); url.searchParams.delete('token');
window.history.replaceState({}, document.title, url.toString()); window.history.replaceState({}, document.title, url.toString());
@@ -411,7 +429,7 @@ export class AppComponent implements OnDestroy {
} }
saveArchiveDestination(): void { saveArchiveDestination(): void {
localStorage.setItem(ARCHIVE_DESTINATION_KEY, this.archiveDestination); setStorageItem(ARCHIVE_DESTINATION_KEY, this.archiveDestination);
} }
refreshArchive(): void { refreshArchive(): void {
@@ -769,7 +787,7 @@ export class AppComponent implements OnDestroy {
} }
private saveWorkspaceState(): void { private saveWorkspaceState(): void {
localStorage.setItem( setStorageItem(
WORKSPACE_TABS_STORAGE_KEY, WORKSPACE_TABS_STORAGE_KEY,
JSON.stringify(this.workspaceTabsState), JSON.stringify(this.workspaceTabsState),
); );
@@ -3,7 +3,13 @@ import { HttpInterceptorFn } from '@angular/common/http';
const TOKEN_STORAGE_KEY = 'spooty_auth_token'; const TOKEN_STORAGE_KEY = 'spooty_auth_token';
export const authTokenInterceptor: HttpInterceptorFn = (req, next) => { export const authTokenInterceptor: HttpInterceptorFn = (req, next) => {
const token = localStorage.getItem(TOKEN_STORAGE_KEY); let token: string | null = null;
try {
token = localStorage.getItem(TOKEN_STORAGE_KEY);
} catch (error) {
console.error('Failed to read auth token from localStorage', error);
}
if (!token) { if (!token) {
return next(req); return next(req);
+15
View File
@@ -8,6 +8,21 @@
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
</head> </head>
<body> <body>
<div id="boot-status">Loading Jarri Spooty...</div>
<app-root></app-root> <app-root></app-root>
<script>
window.addEventListener('error', function (event) {
var boot = document.getElementById('boot-status');
if (boot) {
boot.textContent = 'Frontend error: ' + (event.message || 'unknown');
}
});
window.addEventListener('unhandledrejection', function (event) {
var boot = document.getElementById('boot-status');
if (boot) {
boot.textContent = 'Frontend rejection: ' + String(event.reason || 'unknown');
}
});
</script>
</body> </body>
</html> </html>
+3
View File
@@ -5,6 +5,9 @@ import { AppComponent } from './app/app.component';
import { devTools } from '@ngneat/elf-devtools'; import { devTools } from '@ngneat/elf-devtools';
bootstrapApplication(AppComponent, appConfig) bootstrapApplication(AppComponent, appConfig)
.then(() => {
document.getElementById('boot-status')?.remove();
})
.catch((err) => console.error(err)); .catch((err) => console.error(err));
if (isDevMode()) { if (isDevMode()) {