1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-27 23:03:45 +00:00

feat: use new service as the init trigger

This commit is contained in:
Andreas Coroiu
2026-01-19 16:14:36 +01:00
parent 6d3905c258
commit 6b30234614
3 changed files with 64 additions and 45 deletions

View File

@@ -1,6 +1,7 @@
import { Inject, Injectable, DOCUMENT } from "@angular/core";
import { Inject, Injectable, DOCUMENT, Type } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { Initializable } from "@bitwarden/angular/platform/abstractions/decentralized-init.service";
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
@@ -31,7 +32,7 @@ import { BiometricMessageHandlerService } from "../../services/biometric-message
import { NativeMessagingService } from "../../services/native-messaging.service";
@Injectable()
export class InitService {
export class InitService implements Initializable {
constructor(
@Inject(WINDOW) private win: Window,
private syncService: SyncServiceAbstraction,
@@ -59,46 +60,46 @@ export class InitService {
private readonly migrationRunner: MigrationRunner,
) {}
init() {
return async () => {
await this.sdkLoadService.loadAndInit();
await this.sshAgentService.init();
this.nativeMessagingService.init();
await this.migrationRunner.waitForCompletion(); // Desktop will run migrations in the main process
this.encryptService.init(this.configService);
dependencies: Type<Initializable>[] = [];
const accounts = await firstValueFrom(this.accountService.accounts$);
const setUserKeyInMemoryPromises = [];
for (const userId of Object.keys(accounts) as UserId[]) {
// For each acct, we must await the process of setting the user key in memory
// if the auto user key is set to avoid race conditions of any code trying to access
// the user key from mem.
setUserKeyInMemoryPromises.push(
this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(userId),
);
}
await Promise.all(setUserKeyInMemoryPromises);
async init() {
await this.sdkLoadService.loadAndInit();
await this.sshAgentService.init();
this.nativeMessagingService.init();
await this.migrationRunner.waitForCompletion(); // Desktop will run migrations in the main process
this.encryptService.init(this.configService);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.syncService.fullSync(true);
await this.vaultTimeoutService.init(true);
await (this.i18nService as I18nRendererService).init();
(this.eventUploadService as EventUploadService).init(true);
this.twoFactorService.init();
this.notificationsService.startListening();
const htmlEl = this.win.document.documentElement;
htmlEl.classList.add("os_" + this.platformUtilsService.getDeviceString());
this.themingService.applyThemeChangesTo(this.document);
const accounts = await firstValueFrom(this.accountService.accounts$);
const setUserKeyInMemoryPromises = [];
for (const userId of Object.keys(accounts) as UserId[]) {
// For each acct, we must await the process of setting the user key in memory
// if the auto user key is set to avoid race conditions of any code trying to access
// the user key from mem.
setUserKeyInMemoryPromises.push(
this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(userId),
);
}
await Promise.all(setUserKeyInMemoryPromises);
this.versionService.init();
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.syncService.fullSync(true);
await this.vaultTimeoutService.init(true);
await (this.i18nService as I18nRendererService).init();
(this.eventUploadService as EventUploadService).init(true);
this.twoFactorService.init();
this.notificationsService.startListening();
const htmlEl = this.win.document.documentElement;
htmlEl.classList.add("os_" + this.platformUtilsService.getDeviceString());
this.themingService.applyThemeChangesTo(this.document);
const containerService = new ContainerService(this.keyService, this.encryptService);
containerService.attachToGlobal(this.win);
this.versionService.init();
await this.biometricMessageHandlerService.init();
await this.autofillService.init();
await this.autotypeService.init();
};
const containerService = new ContainerService(this.keyService, this.encryptService);
containerService.attachToGlobal(this.win);
await this.biometricMessageHandlerService.init();
await this.autofillService.init();
await this.autotypeService.init();
}
}

View File

@@ -1,11 +1,15 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { APP_INITIALIZER, NgModule } from "@angular/core";
import { inject, NgModule, provideAppInitializer } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { Subject, merge } from "rxjs";
import { CollectionService, OrganizationUserApiService } from "@bitwarden/admin-console/common";
import { SetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction";
import {
DecentralizedInitService,
initializableProvider,
} from "@bitwarden/angular/platform/abstractions/decentralized-init.service";
import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
import {
SECURE_STORAGE,
@@ -194,12 +198,10 @@ const safeProviders: SafeProvider[] = [
safeProvider(BiometricMessageHandlerService),
safeProvider(SearchBarService),
safeProvider(DialogService),
safeProvider({
provide: APP_INITIALIZER as SafeInjectionToken<() => void>,
useFactory: (initService: InitService) => initService.init(),
deps: [InitService],
multi: true,
}),
provideAppInitializer(() => {
const initService = inject(DecentralizedInitService);
return initService.init();
}) as any,
safeProvider({
provide: RELOAD_CALLBACK,
useValue: null,
@@ -559,6 +561,7 @@ const safeProviders: SafeProvider[] = [
LogService,
],
}),
initializableProvider(InitService),
];
@NgModule({

View File

@@ -1,5 +1,7 @@
import { InjectionToken, Type } from "@angular/core";
import { SafeProvider } from "../utils/safe-provider";
/**
* Services that implement Initializable can participate in decentralized initialization.
* Each service declares its dependencies, and the DecentralizedInitService will execute
@@ -33,6 +35,19 @@ export abstract class Initializable {
*/
export const INIT_SERVICES = new InjectionToken<Initializable[]>("INIT_SERVICES");
/**
* Helper function to create a type-safe provider for an Initializable service.
*
* @param type The Initializable service class
*/
export function initializableProvider<T extends Type<Initializable>>(ctor: T) {
return {
provide: INIT_SERVICES,
useExisting: ctor,
multi: true,
} as SafeProvider;
}
/**
* Service responsible for coordinating decentralized initialization.
* Discovers all registered Initializable services and executes their init()