1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-06 02:23:44 +00:00
Files
browser/apps/web/src/app/billing/organizations/organization-subscription-selfhost.component.ts
Oscar Hinton ac49e594c1 Add standalone false to all non migrated (#14797)
Adds standalone: false to all components since Angular is changing the default to true and we'd rather not have the angular PR change 300+ files.
2025-05-15 10:44:07 -04:00

208 lines
7.5 KiB
TypeScript

// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, OnDestroy, OnInit } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
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";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { OrganizationConnectionType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { OrganizationConnectionResponse } from "@bitwarden/common/admin-console/models/response/organization-connection.response";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { BillingSyncConfigApi } from "@bitwarden/common/billing/models/api/billing-sync-config.api";
import { SelfHostedOrganizationSubscriptionView } from "@bitwarden/common/billing/models/view/self-hosted-organization-subscription.view";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { BillingSyncKeyComponent } from "./billing-sync-key.component";
// FIXME: update to use a const object instead of a typescript enum
// eslint-disable-next-line @bitwarden/platform/no-enums
enum LicenseOptions {
SYNC = 0,
UPLOAD = 1,
}
@Component({
templateUrl: "organization-subscription-selfhost.component.html",
standalone: false,
})
export class OrganizationSubscriptionSelfhostComponent implements OnInit, OnDestroy {
subscription: SelfHostedOrganizationSubscriptionView;
organizationId: string;
userOrg: Organization;
cloudWebVaultUrl: string;
showAutomaticSyncAndManualUpload: boolean;
licenseOptions = LicenseOptions;
form = new FormGroup({
updateMethod: new FormControl(LicenseOptions.UPLOAD),
});
disableLicenseSyncControl = false;
firstLoaded = false;
loading = false;
private _existingBillingSyncConnection: OrganizationConnectionResponse<BillingSyncConfigApi>;
private destroy$ = new Subject<void>();
set existingBillingSyncConnection(value: OrganizationConnectionResponse<BillingSyncConfigApi>) {
this._existingBillingSyncConnection = value;
this.form
.get("updateMethod")
.setValue(this.billingSyncEnabled ? LicenseOptions.SYNC : LicenseOptions.UPLOAD);
}
get existingBillingSyncConnection() {
return this._existingBillingSyncConnection;
}
get billingSyncEnabled() {
return this.existingBillingSyncConnection?.enabled;
}
/**
* Render the subscription as expired.
*/
get showAsExpired() {
return this.subscription.hasSeparateGracePeriod
? this.subscription.isExpiredWithoutGracePeriod
: this.subscription.isExpiredAndOutsideGracePeriod;
}
constructor(
private messagingService: MessagingService,
private apiService: ApiService,
private organizationService: OrganizationService,
private accountService: AccountService,
private route: ActivatedRoute,
private organizationApiService: OrganizationApiServiceAbstraction,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private environmentService: EnvironmentService,
private dialogService: DialogService,
private toastService: ToastService,
) {}
async ngOnInit() {
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
this.route.params
.pipe(
concatMap(async (params) => {
this.organizationId = params.organizationId;
await this.load();
await this.loadOrganizationConnection();
this.firstLoaded = true;
}),
takeUntil(this.destroy$),
)
.subscribe();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
async load() {
if (this.loading) {
return;
}
this.loading = true;
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.userOrg = await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(this.organizationId)),
);
this.showAutomaticSyncAndManualUpload =
this.userOrg.productTierType == ProductTierType.Families ? false : true;
if (this.userOrg.canViewSubscription) {
const subscriptionResponse = await this.organizationApiService.getSubscription(
this.organizationId,
);
this.subscription = new SelfHostedOrganizationSubscriptionView(subscriptionResponse);
}
this.loading = false;
}
async loadOrganizationConnection() {
if (!this.firstLoaded) {
const cloudCommunicationEnabled = await this.apiService.getCloudCommunicationsEnabled();
this.disableLicenseSyncControl = !cloudCommunicationEnabled;
}
if (this.disableLicenseSyncControl) {
return;
}
this.existingBillingSyncConnection = await this.apiService.getOrganizationConnection(
this.organizationId,
OrganizationConnectionType.CloudBillingSync,
BillingSyncConfigApi,
);
}
licenseUploaded() {
// 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.load();
this.messagingService.send("updatedOrgLicense");
}
manageBillingSyncSelfHosted() {
BillingSyncKeyComponent.open(this.dialogService, {
entityId: this.organizationId,
existingConnectionId: this.existingBillingSyncConnection?.id,
billingSyncKey: this.existingBillingSyncConnection?.config?.billingSyncKey,
setParentConnection: (connection: OrganizationConnectionResponse<BillingSyncConfigApi>) => {
this.existingBillingSyncConnection = connection;
},
});
}
syncLicense = async () => {
this.form.get("updateMethod").setValue(LicenseOptions.SYNC);
await this.organizationApiService.selfHostedSyncLicense(this.organizationId);
// 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.load();
await this.loadOrganizationConnection();
this.messagingService.send("updatedOrgLicense");
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("licenseSyncSuccess"),
});
};
get billingSyncSetUp() {
return this.existingBillingSyncConnection?.id != null;
}
get updateMethod() {
return this.form.get("updateMethod").value;
}
get lastLicenseSync() {
return this.existingBillingSyncConnection?.config?.lastLicenseSync;
}
}