mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-5979] Refactor EnvironmentService (#8040)
Refactor environment service to emit a single observable. This required significant changes to how the environment service behaves and tackles much of the tech debt planned for it.
This commit is contained in:
@@ -10,6 +10,15 @@
|
||||
"proxyNotifications": "http://localhost:61840",
|
||||
"wsConnectSrc": "ws://localhost:61840"
|
||||
},
|
||||
"additionalRegions": [
|
||||
{
|
||||
"key": "LOCAL",
|
||||
"domain": "localhost",
|
||||
"urls": {
|
||||
"webVault": "https://localhost:8080"
|
||||
}
|
||||
}
|
||||
],
|
||||
"flags": {
|
||||
"secretsManager": true,
|
||||
"showPasswordless": true,
|
||||
|
||||
@@ -4,6 +4,22 @@
|
||||
"notifications": "https://notifications.euqa.bitwarden.pw",
|
||||
"scim": "https://scim.euqa.bitwarden.pw"
|
||||
},
|
||||
"additionalRegions": [
|
||||
{
|
||||
"key": "USQA",
|
||||
"domain": "qa.bitwarden.pw",
|
||||
"urls": {
|
||||
"webVault": "https://vault.qa.bitwarden.pw"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "EUQA",
|
||||
"domain": "euqa.bitwarden.pw",
|
||||
"urls": {
|
||||
"webVault": "https://vault.euqa.bitwarden.pw"
|
||||
}
|
||||
}
|
||||
],
|
||||
"flags": {
|
||||
"secretsManager": true,
|
||||
"showPasswordless": true
|
||||
|
||||
@@ -10,6 +10,22 @@
|
||||
"proxyEvents": "https://events.qa.bitwarden.pw",
|
||||
"proxyNotifications": "https://notifications.qa.bitwarden.pw"
|
||||
},
|
||||
"additionalRegions": [
|
||||
{
|
||||
"key": "USQA",
|
||||
"domain": "qa.bitwarden.pw",
|
||||
"urls": {
|
||||
"webVault": "https://vault.qa.bitwarden.pw"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "EUQA",
|
||||
"domain": "euqa.bitwarden.pw",
|
||||
"urls": {
|
||||
"webVault": "https://vault.euqa.bitwarden.pw"
|
||||
}
|
||||
}
|
||||
],
|
||||
"flags": {
|
||||
"secretsManager": true,
|
||||
"showPasswordless": true,
|
||||
|
||||
@@ -127,7 +127,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent implements OnDest
|
||||
await this.submit();
|
||||
};
|
||||
|
||||
override launchDuoFrameless() {
|
||||
override async launchDuoFrameless() {
|
||||
const duoHandOffMessage = {
|
||||
title: this.i18nService.t("youSuccessfullyLoggedIn"),
|
||||
message: this.i18nService.t("thisWindowWillCloseIn5Seconds"),
|
||||
|
||||
@@ -44,11 +44,11 @@ export class PremiumComponent implements OnInit {
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
this.selfHosted = platformUtilsService.isSelfHost();
|
||||
this.cloudWebVaultUrl = this.environmentService.getCloudWebVaultUrl();
|
||||
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
|
||||
if (await firstValueFrom(this.billingAccountProfileStateService.hasPremiumPersonally$)) {
|
||||
// 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
|
||||
|
||||
@@ -49,10 +49,10 @@ export class UserSubscriptionComponent implements OnInit {
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
this.selfHosted = platformUtilsService.isSelfHost();
|
||||
this.cloudWebVaultUrl = this.environmentService.getCloudWebVaultUrl();
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
|
||||
this.presentUserWithOffboardingSurvey$ = this.configService.getFeatureFlag$<boolean>(
|
||||
FeatureFlag.AC1607_PresentUserOffboardingSurvey,
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { FormControl, FormGroup } from "@angular/forms";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { concatMap, Subject, takeUntil } from "rxjs";
|
||||
import { concatMap, firstValueFrom, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
@@ -82,11 +82,11 @@ export class OrganizationSubscriptionSelfhostComponent implements OnInit, OnDest
|
||||
private i18nService: I18nService,
|
||||
private environmentService: EnvironmentService,
|
||||
private dialogService: DialogService,
|
||||
) {
|
||||
this.cloudWebVaultUrl = this.environmentService.getCloudWebVaultUrl();
|
||||
}
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
|
||||
|
||||
this.route.params
|
||||
.pipe(
|
||||
concatMap(async (params) => {
|
||||
|
||||
@@ -14,11 +14,15 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||
import { BitPayInvoiceRequest } from "@bitwarden/common/billing/models/request/bit-pay-invoice.request";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { PayPalConfig } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
|
||||
export type PayPalConfig = {
|
||||
businessId?: string;
|
||||
buttonAction?: string;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-add-credit",
|
||||
templateUrl: "add-credit.component.html",
|
||||
|
||||
@@ -1,38 +1,25 @@
|
||||
<div class="tw-mb-1" *ngIf="showRegionSelector">
|
||||
<bit-menu #environmentOptions>
|
||||
<a
|
||||
*ngFor="let region of availableRegions"
|
||||
bitMenuItem
|
||||
[attr.href]="
|
||||
isUsServer ? 'javascript:void(0)' : 'https://vault.bitwarden.com' + routeAndParams
|
||||
region == currentRegion ? 'javascript:void(0)' : region.urls.webVault + routeAndParams
|
||||
"
|
||||
class="pr-4"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw bwi-sm bwi-check pb-1"
|
||||
aria-hidden="true"
|
||||
[style.visibility]="isUsServer ? 'visible' : 'hidden'"
|
||||
[style.visibility]="region == currentRegion ? 'visible' : 'hidden'"
|
||||
></i>
|
||||
{{ "usDomain" | i18n }}
|
||||
</a>
|
||||
<a
|
||||
bitMenuItem
|
||||
[attr.href]="
|
||||
isEuServer ? 'javascript:void(0)' : 'https://vault.bitwarden.eu' + routeAndParams
|
||||
"
|
||||
class="pr-4"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw bwi-sm bwi-check pb-1"
|
||||
aria-hidden="true"
|
||||
[style.visibility]="isEuServer ? 'visible' : 'hidden'"
|
||||
></i>
|
||||
{{ "euDomain" | i18n }}
|
||||
{{ region.domain }}
|
||||
</a>
|
||||
</bit-menu>
|
||||
<div>
|
||||
{{ "server" | i18n }}:
|
||||
<a [routerLink]="[]" [bitMenuTriggerFor]="environmentOptions">
|
||||
<b>{{ isEuServer ? ("euDomain" | i18n) : ("usDomain" | i18n) }}</b
|
||||
<b>{{ currentRegion?.domain }}</b
|
||||
><i class="bwi bwi-fw bwi-sm bwi-angle-down" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { RegionDomain } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import {
|
||||
EnvironmentService,
|
||||
RegionConfig,
|
||||
} from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
|
||||
@@ -12,19 +15,21 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
export class EnvironmentSelectorComponent implements OnInit {
|
||||
constructor(
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private environmentService: EnvironmentService,
|
||||
private router: Router,
|
||||
) {}
|
||||
|
||||
isEuServer: boolean;
|
||||
isUsServer: boolean;
|
||||
showRegionSelector = false;
|
||||
routeAndParams: string;
|
||||
protected availableRegions = this.environmentService.availableRegions();
|
||||
protected currentRegion?: RegionConfig;
|
||||
|
||||
protected showRegionSelector = false;
|
||||
protected routeAndParams: string;
|
||||
|
||||
async ngOnInit() {
|
||||
const domain = Utils.getDomain(window.location.href);
|
||||
this.isEuServer = domain.includes(RegionDomain.EU);
|
||||
this.isUsServer = domain.includes(RegionDomain.US) || domain.includes(RegionDomain.USQA);
|
||||
this.showRegionSelector = !this.platformUtilsService.isSelfHost();
|
||||
this.routeAndParams = `/#${this.router.url}`;
|
||||
|
||||
const host = Utils.getHost(window.location.href);
|
||||
this.currentRegion = this.availableRegions.find((r) => Utils.getHost(r.urls.webVault) === host);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,14 @@ import {
|
||||
OBSERVABLE_MEMORY_STORAGE,
|
||||
OBSERVABLE_DISK_STORAGE,
|
||||
OBSERVABLE_DISK_LOCAL_STORAGE,
|
||||
WINDOW,
|
||||
} from "@bitwarden/angular/services/injection-tokens";
|
||||
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
|
||||
import { ModalService as ModalServiceAbstraction } from "@bitwarden/angular/services/modal.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { LoginService } from "@bitwarden/common/auth/services/login.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@@ -28,9 +31,9 @@ import { StateFactory } from "@bitwarden/common/platform/factories/state-factory
|
||||
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
|
||||
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
|
||||
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
||||
/* eslint-disable import/no-restricted-paths -- Implementation for memory storage */
|
||||
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
|
||||
import { GlobalStateProvider } from "@bitwarden/common/platform/state";
|
||||
/* eslint-disable import/no-restricted-paths -- Implementation for memory storage */
|
||||
import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service";
|
||||
/* eslint-enable import/no-restricted-paths -- Implementation for memory storage */
|
||||
import {
|
||||
@@ -41,6 +44,7 @@ import {
|
||||
import { PolicyListService } from "../admin-console/core/policy-list.service";
|
||||
import { HtmlStorageService } from "../core/html-storage.service";
|
||||
import { I18nService } from "../core/i18n.service";
|
||||
import { WebEnvironmentService } from "../platform/web-environment.service";
|
||||
import { WebMigrationRunner } from "../platform/web-migration-runner";
|
||||
import { WebStorageServiceProvider } from "../platform/web-storage-service.provider";
|
||||
import { WindowStorageService } from "../platform/window-storage.service";
|
||||
@@ -138,6 +142,11 @@ import { WebPlatformUtilsService } from "./web-platform-utils.service";
|
||||
OBSERVABLE_DISK_LOCAL_STORAGE,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: EnvironmentService,
|
||||
useClass: WebEnvironmentService,
|
||||
deps: [WINDOW, StateProvider, AccountService],
|
||||
},
|
||||
{
|
||||
provide: ThemeStateService,
|
||||
useFactory: (globalStateProvider: GlobalStateProvider) =>
|
||||
|
||||
@@ -8,10 +8,6 @@ import { NotificationsService as NotificationsServiceAbstraction } from "@bitwar
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
import {
|
||||
EnvironmentService as EnvironmentServiceAbstraction,
|
||||
Urls,
|
||||
} from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
|
||||
@@ -23,7 +19,6 @@ import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/va
|
||||
export class InitService {
|
||||
constructor(
|
||||
@Inject(WINDOW) private win: Window,
|
||||
private environmentService: EnvironmentServiceAbstraction,
|
||||
private notificationsService: NotificationsServiceAbstraction,
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private i18nService: I18nServiceAbstraction,
|
||||
@@ -41,13 +36,6 @@ export class InitService {
|
||||
return async () => {
|
||||
await this.stateService.init();
|
||||
|
||||
const urls = process.env.URLS as Urls;
|
||||
urls.base ??= this.win.location.origin;
|
||||
await this.environmentService.setUrls(urls);
|
||||
// Workaround to ignore stateService.activeAccount until process.env.URLS are set
|
||||
// TODO: Remove this when implementing ticket PM-2637
|
||||
this.environmentService.initialized = true;
|
||||
|
||||
setTimeout(() => this.notificationsService.init(), 3000);
|
||||
await this.vaultTimeoutService.init(true);
|
||||
await this.i18nService.init();
|
||||
|
||||
62
apps/web/src/app/platform/web-environment.service.ts
Normal file
62
apps/web/src/app/platform/web-environment.service.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { ReplaySubject } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import {
|
||||
Environment,
|
||||
Region,
|
||||
RegionConfig,
|
||||
Urls,
|
||||
} from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import {
|
||||
CloudEnvironment,
|
||||
DefaultEnvironmentService,
|
||||
SelfHostedEnvironment,
|
||||
} from "@bitwarden/common/platform/services/default-environment.service";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
|
||||
/**
|
||||
* Web specific environment service. Ensures that the urls are set from the window location.
|
||||
*/
|
||||
export class WebEnvironmentService extends DefaultEnvironmentService {
|
||||
constructor(
|
||||
private win: Window,
|
||||
stateProvider: StateProvider,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(stateProvider, accountService);
|
||||
|
||||
// The web vault always uses the current location as the base url
|
||||
const urls = process.env.URLS as Urls;
|
||||
urls.base ??= this.win.location.origin;
|
||||
|
||||
// Find the region
|
||||
const domain = Utils.getDomain(this.win.location.href);
|
||||
const region = this.availableRegions().find((r) => Utils.getDomain(r.urls.webVault) === domain);
|
||||
|
||||
let environment: Environment;
|
||||
if (region) {
|
||||
environment = new WebCloudEnvironment(region, urls);
|
||||
} else {
|
||||
environment = new SelfHostedEnvironment(urls);
|
||||
}
|
||||
|
||||
// Override the environment observable with a replay subject
|
||||
const subject = new ReplaySubject<Environment>(1);
|
||||
subject.next(environment);
|
||||
this.environment$ = subject.asObservable();
|
||||
}
|
||||
|
||||
// Web cannot set environment
|
||||
async setEnvironment(region: Region, urls?: Urls): Promise<Urls> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class WebCloudEnvironment extends CloudEnvironment {
|
||||
constructor(config: RegionConfig, urls: Urls) {
|
||||
super(config);
|
||||
// We override the urls to avoid CORS issues
|
||||
this.urls = urls;
|
||||
}
|
||||
}
|
||||
@@ -7063,12 +7063,6 @@
|
||||
"enforceOnLoginDesc": {
|
||||
"message": "Require existing members to change their passwords"
|
||||
},
|
||||
"usDomain": {
|
||||
"message": "bitwarden.com"
|
||||
},
|
||||
"euDomain": {
|
||||
"message": "bitwarden.eu"
|
||||
},
|
||||
"smProjectDeleteAccessRestricted": {
|
||||
"message": "You don't have permissions to delete this project",
|
||||
"description": "The individual description shown to the user when the user doesn't have access to delete a project."
|
||||
|
||||
@@ -171,6 +171,7 @@ const plugins = [
|
||||
PAYPAL_CONFIG: envConfig["paypal"] ?? {},
|
||||
FLAGS: envConfig["flags"] ?? {},
|
||||
DEV_FLAGS: NODE_ENV === "development" ? envConfig["devFlags"] : {},
|
||||
ADDITIONAL_REGIONS: envConfig["additionalRegions"] ?? [],
|
||||
}),
|
||||
new AngularWebpackPlugin({
|
||||
tsConfigPath: "tsconfig.json",
|
||||
|
||||
Reference in New Issue
Block a user