Files
ISA-Frontend/apps/isa-app/src/app/app.module.ts
Lorenz Hilpert 827828aee2 bugfix(auth): enhance authentication flow and error handling
- Ensure access token is present during initialization.
- Improve error logging for identity claims validation.
- Update dependencies for better compatibility.
2025-10-01 14:52:10 +02:00

269 lines
8.8 KiB
TypeScript

import { version } from '../../../../package.json';
import {
HTTP_INTERCEPTORS,
provideHttpClient,
withInterceptorsFromDi,
} from '@angular/common/http';
import {
DEFAULT_CURRENCY_CODE,
ErrorHandler,
Injector,
LOCALE_ID,
NgModule,
inject,
provideAppInitializer,
} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { PlatformModule } from '@angular/cdk/platform';
import { Config } from '@core/config';
import { AuthModule, AuthService, LoginStrategy } from '@core/auth';
import { CoreCommandModule } from '@core/command';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {
ApplicationService,
ApplicationServiceAdapter,
CoreApplicationModule,
} from '@core/application';
import { AppStoreModule } from './app-store.module';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
import { AppSwaggerModule } from './app-swagger.module';
import { AppDomainModule } from './app-domain.module';
import { UiModalModule } from '@ui/modal';
import {
NotificationsHubModule,
NOTIFICATIONS_HUB_OPTIONS,
} from '@hub/notifications';
import { SignalRHubOptions } from '@core/signalr';
import { CoreBreadcrumbModule } from '@core/breadcrumb';
import { UiCommonModule } from '@ui/common';
import { registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import localeDeExtra from '@angular/common/locales/extra/de';
import { HttpErrorInterceptor } from './interceptors';
import { CoreLoggerModule, LOG_PROVIDER } from '@core/logger';
import { IsaLogProvider } from './providers';
import { IsaErrorHandler } from './providers/isa.error-handler';
import {
ScanAdapterModule,
ScanAdapterService,
ScanditScanAdapterModule,
} from '@adapter/scan';
import * as Commands from './commands';
import { PreviewComponent } from './preview';
import { NativeContainerService } from '@external/native-container';
import { ShellModule } from '@shared/shell';
import { MainComponent } from './main.component';
import { IconModule } from '@shared/components/icon';
import { NgIconsModule } from '@ng-icons/core';
import {
matClose,
matWifi,
matWifiOff,
} from '@ng-icons/material-icons/baseline';
import { NetworkStatusService } from './services/network-status.service';
import { debounceTime, firstValueFrom } from 'rxjs';
import { provideMatomo } from 'ngx-matomo-client';
import { withRouter, withRouteData } from 'ngx-matomo-client';
import {
provideLogging,
withLogLevel,
LogLevel,
withSink,
ConsoleLogSink,
} from '@isa/core/logging';
import { IDBStorageProvider, UserStorageProvider } from '@isa/core/storage';
import { Store } from '@ngrx/store';
import { TabNavigationService } from '@isa/core/tabs';
registerLocaleData(localeDe, localeDeExtra);
registerLocaleData(localeDe, 'de', localeDeExtra);
export function _appInitializerFactory(config: Config, injector: Injector) {
return async () => {
const statusElement = document.querySelector('#init-status');
const laoderElement = document.querySelector('#init-loader');
try {
let online = false;
const networkStatus = injector.get(NetworkStatusService);
while (!online) {
online = await firstValueFrom(networkStatus.online$);
if (!online) {
statusElement.innerHTML =
'<b>Warte auf Netzwerkverbindung (WLAN)</b><br><br>Bitte prüfen Sie die Netzwerkverbindung (WLAN).<br>Sobald eine Netzwerkverbindung besteht, wird die App automatisch neu geladen.';
await new Promise((resolve) => setTimeout(resolve, 250));
}
}
statusElement.innerHTML = 'Konfigurationen werden geladen...';
statusElement.innerHTML = 'Scanner wird initialisiert...';
const scanAdapter = injector.get(ScanAdapterService);
await scanAdapter.init();
statusElement.innerHTML = 'Authentifizierung wird geprüft...';
const auth = injector.get(AuthService);
try {
await auth.init();
} catch {
statusElement.innerHTML = 'Authentifizierung wird durchgeführt...';
const strategy = injector.get(LoginStrategy);
await strategy.login();
return;
}
statusElement.innerHTML = 'Native Container wird initialisiert...';
const nativeContainer = injector.get(NativeContainerService);
await nativeContainer.init();
statusElement.innerHTML = 'Datenbank wird initialisiert...';
await injector.get(IDBStorageProvider).init();
statusElement.innerHTML = 'Benutzerzustand wird geladen...';
const userStorage = injector.get(UserStorageProvider);
await userStorage.init();
const store = injector.get(Store);
// Hydrate Ngrx Store
const state = userStorage.get('store');
if (state && state['version'] === version) {
store.dispatch({ type: 'HYDRATE', payload: userStorage.get('store') });
}
// Subscribe on Store changes and save to user storage
store.pipe(debounceTime(1000)).subscribe((state) => {
userStorage.set('store', { ...state, version });
});
// Inject tab navigation service to initialize it
injector.get(TabNavigationService).init();
} catch (error) {
console.error('Error during app initialization', error);
laoderElement.remove();
statusElement.classList.add('text-xl');
statusElement.innerHTML +=
'⚡<br><br><b>Fehler bei der Initialisierung</b><br><br>Bitte prüfen Sie die Netzwerkverbindung (WLAN).<br><br>';
const reload = document.createElement('button');
reload.classList.add(
'bg-brand',
'text-white',
'p-2',
'rounded',
'cursor-pointer',
);
reload.innerHTML = 'App neu laden';
reload.onclick = () => window.location.reload();
statusElement.appendChild(reload);
const preLabel = document.createElement('div');
preLabel.classList.add('mt-12');
preLabel.innerHTML = 'Fehlermeldung:';
statusElement.appendChild(preLabel);
const pre = document.createElement('pre');
pre.classList.add('mt-4', 'text-wrap');
pre.innerHTML = error.message;
statusElement.appendChild(pre);
console.error('Error during app initialization', error);
throw error;
}
};
}
export function _notificationsHubOptionsFactory(
config: Config,
auth: AuthService,
): SignalRHubOptions {
const options = { ...config.get('hubs').notifications };
options.httpOptions.accessTokenFactory = () => auth.getToken();
return options;
}
@NgModule({
declarations: [AppComponent, MainComponent],
bootstrap: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
ShellModule.forRoot(),
AppRoutingModule,
AppSwaggerModule,
AppDomainModule,
CoreBreadcrumbModule.forRoot(),
CoreCommandModule.forRoot(Object.values(Commands)),
CoreLoggerModule.forRoot(),
AppStoreModule,
PreviewComponent,
AuthModule.forRoot(),
CoreApplicationModule.forRoot(),
UiModalModule.forRoot(),
UiCommonModule.forRoot(),
NotificationsHubModule.forRoot(),
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: environment.production,
registrationStrategy: 'registerWhenStable:30000',
}),
ScanAdapterModule.forRoot(),
ScanditScanAdapterModule.forRoot(),
PlatformModule,
IconModule.forRoot(),
NgIconsModule.withIcons({ matWifiOff, matClose, matWifi }),
],
providers: [
provideAppInitializer(() => {
const initializerFn = _appInitializerFactory(
inject(Config),
inject(Injector),
);
return initializerFn();
}),
{
provide: NOTIFICATIONS_HUB_OPTIONS,
useFactory: _notificationsHubOptionsFactory,
deps: [Config, AuthService],
},
{
provide: HTTP_INTERCEPTORS,
useClass: HttpErrorInterceptor,
multi: true,
},
{
provide: LOG_PROVIDER,
useClass: IsaLogProvider,
multi: true,
},
{
provide: ErrorHandler,
useClass: IsaErrorHandler,
},
{
provide: ApplicationService,
useClass: ApplicationServiceAdapter,
},
{ provide: LOCALE_ID, useValue: 'de-DE' },
provideHttpClient(withInterceptorsFromDi()),
provideMatomo(
{ trackerUrl: 'https://matomo.paragon-data.net', siteId: '1' },
withRouter(),
withRouteData(),
),
provideLogging(withLogLevel(LogLevel.Debug), withSink(ConsoleLogSink)),
{
provide: DEFAULT_CURRENCY_CODE,
useValue: 'EUR',
},
],
})
export class AppModule {}