mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
[PM-8115] Desktop, Extension UI Refresh: Self-hosted Setup Dialog (#11597)
* Reimplement RegistrationSelfHostedEnvConfigDialogComponent * Update EnvironmentSelectorComponent text based on feature flag. * Initialize RegistrationSelfHostedEnvConfigDialog with existing values if self hosted * Cleanup debug * Add comment * Remove changes to home and login components * Remove changes to desktop login component * Remove changes to browser home component * Simplify accessing string. * Add environment selector service. * Cleanup unused imports in environment-selector * Launch new env selector dialog from desktop * Fix lint errors * Address PR feedback: move dialog component, remove EnvironmentSelectorService, remove unused translation string * Remove changes to AnonLayout * PM-8115 - Export Re-usable component from Libs/auth for clean import elsewhere in clients. * Remove unused accessingString variable * Add success toast --------- Co-authored-by: Jared Snider <jsnider@bitwarden.com>
This commit is contained in:
@@ -15,7 +15,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { DialogService, FormFieldModule, SelectModule, ToastService } from "@bitwarden/components";
|
||||
|
||||
import { RegistrationSelfHostedEnvConfigDialogComponent } from "./registration-self-hosted-env-config-dialog.component";
|
||||
import { SelfHostedEnvConfigDialogComponent } from "../../self-hosted-env-config-dialog/self-hosted-env-config-dialog.component";
|
||||
|
||||
/**
|
||||
* Component for selecting the environment to register with in the email verification registration flow.
|
||||
@@ -125,9 +125,7 @@ export class RegistrationEnvSelectorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
if (selectedRegion === Region.SelfHosted) {
|
||||
return from(
|
||||
RegistrationSelfHostedEnvConfigDialogComponent.open(this.dialogService),
|
||||
).pipe(
|
||||
return from(SelfHostedEnvConfigDialogComponent.open(this.dialogService)).pipe(
|
||||
tap((result: boolean | undefined) =>
|
||||
this.handleSelfHostedEnvConfigDialogResult(result, prevSelectedRegion),
|
||||
),
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||
<bit-dialog>
|
||||
<span bitDialogTitle> Self-hosted environment</span>
|
||||
<ng-container bitDialogContent>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "baseUrl" | i18n }}</bit-label>
|
||||
<input
|
||||
id="self_hosted_env_settings_form_input_base_url"
|
||||
bitInput
|
||||
type="text"
|
||||
formControlName="baseUrl"
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
<bit-hint>{{ "selfHostedBaseUrlHint" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
|
||||
<button bitLink linkType="primary" type="button" (click)="showCustomEnv = !showCustomEnv">
|
||||
<i
|
||||
class="bwi bwi-fw bwi-sm"
|
||||
[ngClass]="{ 'bwi-angle-right': !showCustomEnv, 'bwi-angle-down': showCustomEnv }"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{{ "customEnvironment" | i18n }}
|
||||
</button>
|
||||
|
||||
<ng-container *ngIf="showCustomEnv">
|
||||
<p bitTypography="body1" class="tw-text-muted tw-mt-3">
|
||||
{{ "selfHostedCustomEnvHeader" | i18n }}
|
||||
</p>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "webVaultUrl" | i18n }}</bit-label>
|
||||
<input
|
||||
id="self_hosted_env_settings_form_input_web_vault_url"
|
||||
bitInput
|
||||
type="text"
|
||||
formControlName="webVaultUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</bit-form-field>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "apiUrl" | i18n }}</bit-label>
|
||||
<input
|
||||
id="self_hosted_env_settings_form_input_api_url"
|
||||
bitInput
|
||||
type="text"
|
||||
formControlName="apiUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</bit-form-field>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "identityUrl" | i18n }}</bit-label>
|
||||
<input
|
||||
id="self_hosted_env_settings_form_input_identity_url"
|
||||
bitInput
|
||||
type="text"
|
||||
formControlName="identityUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</bit-form-field>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "notificationsUrl" | i18n }}</bit-label>
|
||||
<input
|
||||
id="self_hosted_env_settings_form_input_notifications_url"
|
||||
bitInput
|
||||
type="text"
|
||||
formControlName="notificationsUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</bit-form-field>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "iconsUrl" | i18n }}</bit-label>
|
||||
<input
|
||||
id="self_hosted_env_settings_form_input_icons_url"
|
||||
bitInput
|
||||
type="text"
|
||||
formControlName="iconsUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</bit-form-field>
|
||||
</ng-container>
|
||||
|
||||
<span
|
||||
*ngIf="showErrorSummary"
|
||||
class="tw-block tw-text-danger tw-mt-2"
|
||||
aria-live="assertive"
|
||||
role="alert"
|
||||
>
|
||||
<i class="bwi bwi-error"></i> {{ "selfHostedEnvFormInvalid" | i18n }}
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button type="submit" bitButton bitFormButton buttonType="primary">
|
||||
{{ "save" | i18n }}
|
||||
</button>
|
||||
|
||||
<button type="button" bitButton bitFormButton buttonType="secondary" (click)="cancel()">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
</form>
|
||||
@@ -1,164 +0,0 @@
|
||||
import { DialogRef } from "@angular/cdk/dialog";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import {
|
||||
AbstractControl,
|
||||
FormBuilder,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
ReactiveFormsModule,
|
||||
ValidationErrors,
|
||||
ValidatorFn,
|
||||
} from "@angular/forms";
|
||||
import { Subject, firstValueFrom } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import {
|
||||
EnvironmentService,
|
||||
Region,
|
||||
} from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import {
|
||||
AsyncActionsModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
DialogService,
|
||||
FormFieldModule,
|
||||
LinkModule,
|
||||
TypographyModule,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
/**
|
||||
* Validator for self-hosted environment settings form.
|
||||
* It enforces that at least one URL is provided.
|
||||
*/
|
||||
function selfHostedEnvSettingsFormValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
const formGroup = control as FormGroup;
|
||||
const baseUrl = formGroup.get("baseUrl")?.value;
|
||||
const webVaultUrl = formGroup.get("webVaultUrl")?.value;
|
||||
const apiUrl = formGroup.get("apiUrl")?.value;
|
||||
const identityUrl = formGroup.get("identityUrl")?.value;
|
||||
const iconsUrl = formGroup.get("iconsUrl")?.value;
|
||||
const notificationsUrl = formGroup.get("notificationsUrl")?.value;
|
||||
|
||||
if (baseUrl || webVaultUrl || apiUrl || identityUrl || iconsUrl || notificationsUrl) {
|
||||
return null; // valid
|
||||
} else {
|
||||
return { atLeastOneUrlIsRequired: true }; // invalid
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog for configuring self-hosted environment settings.
|
||||
*/
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: "auth-registration-self-hosted-env-config-dialog",
|
||||
templateUrl: "registration-self-hosted-env-config-dialog.component.html",
|
||||
imports: [
|
||||
CommonModule,
|
||||
JslibModule,
|
||||
DialogModule,
|
||||
ButtonModule,
|
||||
LinkModule,
|
||||
TypographyModule,
|
||||
ReactiveFormsModule,
|
||||
FormFieldModule,
|
||||
AsyncActionsModule,
|
||||
],
|
||||
})
|
||||
export class RegistrationSelfHostedEnvConfigDialogComponent implements OnInit, OnDestroy {
|
||||
/**
|
||||
* Opens the dialog.
|
||||
* @param dialogService - Dialog service.
|
||||
* @returns Promise that resolves to true if the dialog was closed with a successful result, false otherwise.
|
||||
*/
|
||||
static async open(dialogService: DialogService): Promise<boolean> {
|
||||
const dialogRef = dialogService.open<boolean>(RegistrationSelfHostedEnvConfigDialogComponent, {
|
||||
disableClose: false,
|
||||
});
|
||||
|
||||
const dialogResult = await firstValueFrom(dialogRef.closed);
|
||||
|
||||
return dialogResult;
|
||||
}
|
||||
|
||||
formGroup = this.formBuilder.group(
|
||||
{
|
||||
baseUrl: [null],
|
||||
webVaultUrl: [null],
|
||||
apiUrl: [null],
|
||||
identityUrl: [null],
|
||||
iconsUrl: [null],
|
||||
notificationsUrl: [null],
|
||||
},
|
||||
{ validators: selfHostedEnvSettingsFormValidator() },
|
||||
);
|
||||
|
||||
get baseUrl(): FormControl {
|
||||
return this.formGroup.get("baseUrl") as FormControl;
|
||||
}
|
||||
|
||||
get webVaultUrl(): FormControl {
|
||||
return this.formGroup.get("webVaultUrl") as FormControl;
|
||||
}
|
||||
|
||||
get apiUrl(): FormControl {
|
||||
return this.formGroup.get("apiUrl") as FormControl;
|
||||
}
|
||||
|
||||
get identityUrl(): FormControl {
|
||||
return this.formGroup.get("identityUrl") as FormControl;
|
||||
}
|
||||
|
||||
get iconsUrl(): FormControl {
|
||||
return this.formGroup.get("iconsUrl") as FormControl;
|
||||
}
|
||||
|
||||
get notificationsUrl(): FormControl {
|
||||
return this.formGroup.get("notificationsUrl") as FormControl;
|
||||
}
|
||||
|
||||
showCustomEnv = false;
|
||||
showErrorSummary = false;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private dialogRef: DialogRef<boolean>,
|
||||
private formBuilder: FormBuilder,
|
||||
private environmentService: EnvironmentService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
submit = async () => {
|
||||
this.showErrorSummary = false;
|
||||
|
||||
if (this.formGroup.invalid) {
|
||||
this.showErrorSummary = true;
|
||||
return;
|
||||
}
|
||||
|
||||
await this.environmentService.setEnvironment(Region.SelfHosted, {
|
||||
base: this.baseUrl.value,
|
||||
api: this.apiUrl.value,
|
||||
identity: this.identityUrl.value,
|
||||
webVault: this.webVaultUrl.value,
|
||||
icons: this.iconsUrl.value,
|
||||
notifications: this.notificationsUrl.value,
|
||||
});
|
||||
|
||||
this.dialogRef.close(true);
|
||||
};
|
||||
|
||||
async cancel() {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user