mirror of
https://github.com/bitwarden/browser
synced 2026-01-31 00:33:33 +00:00
Remove bulk collection seeder and CipherHealthTest
- Remove bulk-collection-seeder component and its files - Remove bulk-collection-seeder from routing and module - Remove CipherHealthTest from reports.ts and reports-home - Remove cipherHealthTest locale strings - Keep risk-insights-prototype for access intelligence work Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,77 +0,0 @@
|
||||
<bit-section>
|
||||
<bit-section-header>
|
||||
<h2 bitTypography="h2">Bulk Collection Seeder</h2>
|
||||
<p bitTypography="body1" class="tw-text-muted">
|
||||
Create multiple collections at once for testing purposes. Enter one collection name per line.
|
||||
</p>
|
||||
</bit-section-header>
|
||||
|
||||
<div class="tw-flex tw-flex-col tw-gap-4">
|
||||
<bit-form-field>
|
||||
<bit-label>Collection Names (one per line)</bit-label>
|
||||
<textarea
|
||||
bitInput
|
||||
[(ngModel)]="collectionNames"
|
||||
rows="10"
|
||||
placeholder="Collection 1 Collection 2 Parent/Child Collection ..."
|
||||
[disabled]="isProcessing()"
|
||||
></textarea>
|
||||
<bit-hint
|
||||
>Enter collection names, one per line. Use "/" for nested collections (e.g.,
|
||||
"Parent/Child").</bit-hint
|
||||
>
|
||||
</bit-form-field>
|
||||
|
||||
<div class="tw-flex tw-gap-2">
|
||||
<button
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[disabled]="isProcessing() || !collectionNames.trim()"
|
||||
(click)="createCollections()"
|
||||
>
|
||||
<span *ngIf="!isProcessing()">Create Collections</span>
|
||||
<span *ngIf="isProcessing()">Creating...</span>
|
||||
</button>
|
||||
<button
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
[disabled]="isProcessing() || !hasRun()"
|
||||
(click)="clearResults()"
|
||||
>
|
||||
Clear Results
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="isProcessing() || hasRun()" class="tw-mt-4">
|
||||
<bit-progress [barWidth]="progress()"></bit-progress>
|
||||
<p bitTypography="body2" class="tw-mt-2 tw-text-muted">{{ progressMessage() }}</p>
|
||||
</div>
|
||||
|
||||
<div *ngIf="hasRun() && !isProcessing()" class="tw-mt-4">
|
||||
<h3 bitTypography="h3">Results</h3>
|
||||
<p bitTypography="body1">
|
||||
<span class="tw-text-success">{{ successCount }} succeeded</span>
|
||||
<span *ngIf="failureCount > 0"
|
||||
>, <span class="tw-text-danger">{{ failureCount }} failed</span></span
|
||||
>
|
||||
</p>
|
||||
|
||||
<div
|
||||
*ngIf="results().length > 0"
|
||||
class="tw-mt-2 tw-max-h-64 tw-overflow-y-auto tw-border tw-border-secondary-300 tw-rounded tw-p-2"
|
||||
>
|
||||
<div
|
||||
*ngFor="let result of results()"
|
||||
class="tw-flex tw-items-center tw-gap-2 tw-py-1 tw-border-b tw-border-secondary-100 last:tw-border-b-0"
|
||||
>
|
||||
<span *ngIf="result.success" class="tw-text-success">✓</span>
|
||||
<span *ngIf="!result.success" class="tw-text-danger">✗</span>
|
||||
<span>{{ result.name }}</span>
|
||||
<span *ngIf="!result.success" class="tw-text-danger tw-text-sm"
|
||||
>- {{ result.error }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</bit-section>
|
||||
@@ -1,149 +0,0 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
DestroyRef,
|
||||
inject,
|
||||
OnInit,
|
||||
signal,
|
||||
} from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { CollectionAdminService, CollectionAdminView } from "@bitwarden/admin-console/common";
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
ButtonModule,
|
||||
FormFieldModule,
|
||||
ProgressModule,
|
||||
SectionComponent,
|
||||
SectionHeaderComponent,
|
||||
TypographyModule,
|
||||
} from "@bitwarden/components";
|
||||
import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module";
|
||||
|
||||
interface CollectionCreationResult {
|
||||
name: string;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
id?: CollectionId;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-bulk-collection-seeder",
|
||||
templateUrl: "./bulk-collection-seeder.component.html",
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
JslibModule,
|
||||
HeaderModule,
|
||||
ButtonModule,
|
||||
FormFieldModule,
|
||||
ProgressModule,
|
||||
SectionComponent,
|
||||
SectionHeaderComponent,
|
||||
TypographyModule,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BulkCollectionSeederComponent implements OnInit {
|
||||
private destroyRef = inject(DestroyRef);
|
||||
private route = inject(ActivatedRoute);
|
||||
private collectionAdminService = inject(CollectionAdminService);
|
||||
private accountService = inject(AccountService);
|
||||
|
||||
protected organizationId: OrganizationId | null = null;
|
||||
protected collectionNames = "";
|
||||
protected readonly isProcessing = signal(false);
|
||||
protected readonly progress = signal(0);
|
||||
protected readonly progressMessage = signal("");
|
||||
protected readonly results = signal<CollectionCreationResult[]>([]);
|
||||
protected readonly hasRun = signal(false);
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.params.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
|
||||
this.organizationId = params["organizationId"] as OrganizationId;
|
||||
});
|
||||
}
|
||||
|
||||
protected async createCollections(): Promise<void> {
|
||||
if (!this.organizationId || !this.collectionNames.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const names = this.collectionNames
|
||||
.split("\n")
|
||||
.map((name) => name.trim())
|
||||
.filter((name) => name.length > 0);
|
||||
|
||||
if (names.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isProcessing.set(true);
|
||||
this.progress.set(0);
|
||||
this.results.set([]);
|
||||
this.hasRun.set(true);
|
||||
|
||||
const results: CollectionCreationResult[] = [];
|
||||
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const name = names[i];
|
||||
this.progressMessage.set(`Creating collection ${i + 1} of ${names.length}: ${name}`);
|
||||
this.progress.set(Math.round(((i + 1) / names.length) * 100));
|
||||
|
||||
try {
|
||||
const collectionView = new CollectionAdminView({
|
||||
id: null as unknown as CollectionId,
|
||||
organizationId: this.organizationId,
|
||||
name: name,
|
||||
});
|
||||
collectionView.groups = [];
|
||||
collectionView.users = [];
|
||||
|
||||
const response = await this.collectionAdminService.create(collectionView, userId);
|
||||
|
||||
results.push({
|
||||
name,
|
||||
success: true,
|
||||
id: response.id,
|
||||
});
|
||||
} catch (error) {
|
||||
results.push({
|
||||
name,
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
}
|
||||
|
||||
this.results.set([...results]);
|
||||
}
|
||||
|
||||
this.progressMessage.set(
|
||||
`Completed: ${results.filter((r) => r.success).length} of ${names.length} collections created`,
|
||||
);
|
||||
this.isProcessing.set(false);
|
||||
}
|
||||
|
||||
protected get successCount(): number {
|
||||
return this.results().filter((r) => r.success).length;
|
||||
}
|
||||
|
||||
protected get failureCount(): number {
|
||||
return this.results().filter((r) => !r.success).length;
|
||||
}
|
||||
|
||||
protected clearResults(): void {
|
||||
this.results.set([]);
|
||||
this.hasRun.set(false);
|
||||
this.progress.set(0);
|
||||
this.progressMessage.set("");
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import { organizationPermissionsGuard } from "../guards/org-permissions.guard";
|
||||
import { organizationRedirectGuard } from "../guards/org-redirect.guard";
|
||||
import { EventsComponent } from "../manage/events.component";
|
||||
|
||||
import { BulkCollectionSeederComponent } from "./bulk-collection-seeder/bulk-collection-seeder.component";
|
||||
import { ReportsHomeComponent } from "./reports-home.component";
|
||||
import { RiskInsightsPrototypeComponent } from "./risk-insights-prototype/risk-insights-prototype.component";
|
||||
|
||||
@@ -92,14 +91,6 @@ const routes: Routes = [
|
||||
},
|
||||
canActivate: [isPaidOrgGuard()],
|
||||
},
|
||||
{
|
||||
path: "bulk-collection-seeder",
|
||||
component: BulkCollectionSeederComponent,
|
||||
data: {
|
||||
titleId: "bulkCollectionSeeder",
|
||||
},
|
||||
canActivate: [isPaidOrgGuard()],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ReportsSharedModule } from "../../../dirt/reports";
|
||||
import { HeaderModule } from "../../../layouts/header/header.module";
|
||||
import { SharedModule } from "../../../shared/shared.module";
|
||||
|
||||
import { BulkCollectionSeederComponent } from "./bulk-collection-seeder/bulk-collection-seeder.component";
|
||||
import { OrganizationReportingRoutingModule } from "./organization-reporting-routing.module";
|
||||
import { ReportsHomeComponent } from "./reports-home.component";
|
||||
import { RiskInsightsPrototypeComponent } from "./risk-insights-prototype/risk-insights-prototype.component";
|
||||
@@ -16,7 +15,6 @@ import { RiskInsightsPrototypeComponent } from "./risk-insights-prototype/risk-i
|
||||
OrganizationReportingRoutingModule,
|
||||
HeaderModule,
|
||||
RiskInsightsPrototypeComponent,
|
||||
BulkCollectionSeederComponent,
|
||||
],
|
||||
declarations: [ReportsHomeComponent],
|
||||
})
|
||||
|
||||
@@ -83,10 +83,6 @@ export class ReportsHomeComponent implements OnInit {
|
||||
? ReportVariant.Enabled
|
||||
: ReportVariant.RequiresEnterprise,
|
||||
},
|
||||
{
|
||||
...reports[ReportType.CipherHealthTest],
|
||||
variant: reportRequiresUpgrade,
|
||||
},
|
||||
{
|
||||
...reports[ReportType.RiskInsightsPrototype],
|
||||
variant: reportRequiresUpgrade,
|
||||
|
||||
@@ -20,7 +20,6 @@ export enum ReportType {
|
||||
Inactive2fa = "inactive2fa",
|
||||
DataBreach = "dataBreach",
|
||||
MemberAccessReport = "memberAccessReport",
|
||||
CipherHealthTest = "cipherHealthTest",
|
||||
RiskInsightsPrototype = "riskInsightsPrototype",
|
||||
}
|
||||
|
||||
@@ -69,12 +68,6 @@ export const reports: Record<ReportType, ReportWithoutVariant> = {
|
||||
route: "member-access-report",
|
||||
icon: UserLockIcon,
|
||||
},
|
||||
[ReportType.CipherHealthTest]: {
|
||||
title: "cipherHealthTest",
|
||||
description: "cipherHealthTestDesc",
|
||||
route: "cipher-health-test",
|
||||
icon: UnlockedIcon,
|
||||
},
|
||||
[ReportType.RiskInsightsPrototype]: {
|
||||
title: "riskInsightsPrototype",
|
||||
description: "riskInsightsPrototypeDesc",
|
||||
|
||||
@@ -10739,12 +10739,6 @@
|
||||
"memberAccessReportAuthenticationEnabledFalse": {
|
||||
"message": "Off"
|
||||
},
|
||||
"cipherHealthTest": {
|
||||
"message": "Risk insights diagnostics"
|
||||
},
|
||||
"cipherHealthTestDesc": {
|
||||
"message": "Test password health and member access mapping in parallel. View detailed performance diagnostics and analyze cipher security risks."
|
||||
},
|
||||
"riskInsightsPrototype": {
|
||||
"message": "Risk Insights Prototype"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user