remove emails from stored tenants
This commit is contained in:
parent
c851ab1394
commit
04a69f5e68
@ -1,11 +1,10 @@
|
|||||||
import { inject, Injectable, signal } from '@angular/core';
|
import { inject, Injectable, signal } from '@angular/core';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { List } from '../../utils';
|
import { List } from '../../utils';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
|
|
||||||
export interface IStoredTenantId {
|
export interface IStoredTenantId {
|
||||||
readonly tenantId: string;
|
readonly tenantId: string;
|
||||||
readonly email: string;
|
|
||||||
readonly created: string;
|
readonly created: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ export class TenantsService {
|
|||||||
removeItem: localStorage.removeItem.bind(localStorage),
|
removeItem: localStorage.removeItem.bind(localStorage),
|
||||||
key: localStorage.key.bind(localStorage),
|
key: localStorage.key.bind(localStorage),
|
||||||
};
|
};
|
||||||
readonly #activeTenantId = signal<string>('');
|
readonly #activeTenantId = signal('');
|
||||||
protected readonly _serviceName: string = 'tenant-user-management';
|
protected readonly _serviceName: string = 'tenant-user-management';
|
||||||
|
|
||||||
get activeTenantId() {
|
get activeTenantId() {
|
||||||
@ -37,15 +36,10 @@ export class TenantsService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
storeTenant(emailOrUsername: string) {
|
storeTenant() {
|
||||||
if (!emailOrUsername) {
|
|
||||||
this.#logger.warn('[TENANTS] Email or username is null, skip storing');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const storedTenants = this.getStoredTenants();
|
const storedTenants = this.getStoredTenants();
|
||||||
const activeTenantId = this.#activeTenantId();
|
const activeTenantId = this.#activeTenantId();
|
||||||
const existing = storedTenants.find(s => s.email === emailOrUsername && s.tenantId === activeTenantId);
|
const existing = storedTenants.find(s => s.tenantId === activeTenantId);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
this.#logger.info('[TENANTS] Stored tenant exists: ', storedTenants);
|
this.#logger.info('[TENANTS] Stored tenant exists: ', storedTenants);
|
||||||
return;
|
return;
|
||||||
@ -56,7 +50,7 @@ export class TenantsService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
storedTenants.push({ tenantId: activeTenantId, email: emailOrUsername, created: new Date().toISOString() });
|
storedTenants.push({ tenantId: activeTenantId, created: new Date().toISOString() });
|
||||||
this.#storageReference.setItem(STORED_TENANTS_KEY, JSON.stringify(storedTenants));
|
this.#storageReference.setItem(STORED_TENANTS_KEY, JSON.stringify(storedTenants));
|
||||||
this.#logger.info('[TENANTS] Stored tenants: ', storedTenants);
|
this.#logger.info('[TENANTS] Stored tenants: ', storedTenants);
|
||||||
}
|
}
|
||||||
@ -72,8 +66,8 @@ export class TenantsService {
|
|||||||
const diff = date2.diff(date1, 'days');
|
const diff = date2.diff(date1, 'days');
|
||||||
const is90DaysOld = diff >= 90;
|
const is90DaysOld = diff >= 90;
|
||||||
if (is90DaysOld) {
|
if (is90DaysOld) {
|
||||||
this.#logger.warn(`[TENANTS] Saved tenant ${s.tenantId} - ${s.email} is 90 days old, delete it`);
|
this.#logger.warn(`[TENANTS] Saved tenant ${s.tenantId} is 90 days old, delete it`);
|
||||||
this.removeStored(s.email);
|
this.removeStored(s.tenantId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,17 +77,16 @@ export class TenantsService {
|
|||||||
return validStoredTenants;
|
return validStoredTenants;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeStored(email: string) {
|
removeStored(tenantId: string) {
|
||||||
if (!email) {
|
if (!tenantId) {
|
||||||
this.#logger.warn('[TENANTS] Email is null, skip storing');
|
this.#logger.warn('[TENANTS] Tenant Id is null, skip removing');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const storedTenants = this.getStoredTenants();
|
const storedTenants = this.getStoredTenants();
|
||||||
const activeTenantId = this.#activeTenantId();
|
const existing = storedTenants.find(s => s.tenantId === tenantId);
|
||||||
const existing = storedTenants.find(s => s.email === email && s.tenantId === activeTenantId);
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
this.#logger.info('[TENANTS] No stored tenant for ', email);
|
this.#logger.info('[TENANTS] No stored tenant for ', tenantId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<div *ngIf="storedTenants.length">
|
<div *ngIf="storedTenants.length">
|
||||||
<div
|
<div
|
||||||
(click)="select(stored.tenantId, stored.email)"
|
(click)="select(stored.tenantId)"
|
||||||
*ngFor="let stored of storedTenants"
|
*ngFor="let stored of storedTenants"
|
||||||
class="d-flex pointer mat-elevation-z2 card stored-tenant-card mt-10"
|
class="d-flex pointer mat-elevation-z2 card stored-tenant-card mt-10"
|
||||||
>
|
>
|
||||||
@ -25,10 +25,12 @@
|
|||||||
|
|
||||||
<div class="card-content flex-column">
|
<div class="card-content flex-column">
|
||||||
<span class="heading">{{ stored.tenantId }}</span>
|
<span class="heading">{{ stored.tenantId }}</span>
|
||||||
<span>{{ stored.email }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-icon class="card-icon upside-down" svgIcon="iqser:expand"></mat-icon>
|
<mat-icon class="card-icon upside-down" svgIcon="iqser:expand"></mat-icon>
|
||||||
|
<div class="remove" iqserStopPropagation>
|
||||||
|
<mat-icon (click)="removeStored(stored.tenantId)" svgIcon="iqser:close"></mat-icon>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,13 @@
|
|||||||
.stored-tenant-card {
|
.stored-tenant-card {
|
||||||
width: 450px;
|
width: 450px;
|
||||||
height: 90px;
|
height: 90px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.remove {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
@ -66,3 +73,24 @@
|
|||||||
line-height: 29px;
|
line-height: 29px;
|
||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.remove {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--iqser-accent);
|
||||||
|
color: var(--iqser-white);
|
||||||
|
position: absolute;
|
||||||
|
right: -8px;
|
||||||
|
top: -8px;
|
||||||
|
line-height: 6px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
width: 6px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject, Input } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, inject, Input } from '@angular/core';
|
||||||
import { FormBuilder, Validators } from '@angular/forms';
|
import { FormBuilder, Validators } from '@angular/forms';
|
||||||
import { TenantsService } from '../services';
|
|
||||||
import { LoadingService } from '../../loading';
|
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { LoadingService } from '../../loading';
|
||||||
|
import { getConfig } from '../../services';
|
||||||
import { BASE_HREF } from '../../utils';
|
import { BASE_HREF } from '../../utils';
|
||||||
import { getKeycloakOptions } from '../keycloak-initializer';
|
import { getKeycloakOptions } from '../keycloak-initializer';
|
||||||
|
import { IStoredTenantId, TenantsService } from '../services';
|
||||||
import { KeycloakStatusService } from '../services/keycloak-status.service';
|
import { KeycloakStatusService } from '../services/keycloak-status.service';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
|
||||||
import { getConfig } from '../../services';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './tenant-select.component.html',
|
templateUrl: './tenant-select.component.html',
|
||||||
@ -18,9 +18,8 @@ import { getConfig } from '../../services';
|
|||||||
export class TenantSelectComponent {
|
export class TenantSelectComponent {
|
||||||
protected readonly baseHref = inject(BASE_HREF);
|
protected readonly baseHref = inject(BASE_HREF);
|
||||||
protected readonly logger = inject(NGXLogger);
|
protected readonly logger = inject(NGXLogger);
|
||||||
protected readonly storedTenants = inject(TenantsService)
|
protected readonly tenantsService = inject(TenantsService);
|
||||||
.getStoredTenants()
|
protected storedTenants: IStoredTenantId[] = [];
|
||||||
.sort((a, b) => a.tenantId.localeCompare(b.tenantId));
|
|
||||||
protected readonly titleService = inject(Title);
|
protected readonly titleService = inject(Title);
|
||||||
protected readonly config = getConfig();
|
protected readonly config = getConfig();
|
||||||
protected readonly loadingService = inject(LoadingService);
|
protected readonly loadingService = inject(LoadingService);
|
||||||
@ -32,6 +31,10 @@ export class TenantSelectComponent {
|
|||||||
});
|
});
|
||||||
@Input() isLoggedOut = false;
|
@Input() isLoggedOut = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#loadStoredTenants();
|
||||||
|
}
|
||||||
|
|
||||||
updateTenantSelection() {
|
updateTenantSelection() {
|
||||||
const tenantId = this.form.controls.tenantId.value;
|
const tenantId = this.form.controls.tenantId.value;
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
@ -39,12 +42,10 @@ export class TenantSelectComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.loadingService.start();
|
this.loadingService.start();
|
||||||
|
return this.select(tenantId);
|
||||||
const email = this.#getEmail(tenantId);
|
|
||||||
return this.select(tenantId, email);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async select(tenantId: string, email?: string) {
|
async select(tenantId: string) {
|
||||||
try {
|
try {
|
||||||
this.logger.info('[KEYCLOAK] Initializing keycloak for tenant', tenantId);
|
this.logger.info('[KEYCLOAK] Initializing keycloak for tenant', tenantId);
|
||||||
await this.keycloakService.init(getKeycloakOptions(this.baseHref, this.config, tenantId));
|
await this.keycloakService.init(getKeycloakOptions(this.baseHref, this.config, tenantId));
|
||||||
@ -55,15 +56,18 @@ export class TenantSelectComponent {
|
|||||||
const url = this.keycloakService.getKeycloakInstance().createLoginUrl({
|
const url = this.keycloakService.getKeycloakInstance().createLoginUrl({
|
||||||
redirectUri: this.keycloakStatusService.createLoginUrl(tenantId),
|
redirectUri: this.keycloakStatusService.createLoginUrl(tenantId),
|
||||||
idpHint: this.config.OAUTH_IDP_HINT,
|
idpHint: this.config.OAUTH_IDP_HINT,
|
||||||
loginHint: email ?? undefined,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.info('[KEYCLOAK] Init succeeded. Logout and redirect to', url);
|
this.logger.info('[KEYCLOAK] Init succeeded. Logout and redirect to', url);
|
||||||
return this.keycloakService.logout(url);
|
return this.keycloakService.logout(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
#getEmail(tenantId: string) {
|
removeStored(tenantId: string) {
|
||||||
const existingStored = this.storedTenants.filter(s => s.tenantId === tenantId);
|
this.tenantsService.removeStored(tenantId);
|
||||||
return existingStored.length === 1 ? existingStored[0].email : undefined;
|
this.#loadStoredTenants();
|
||||||
|
}
|
||||||
|
|
||||||
|
#loadStoredTenants() {
|
||||||
|
this.storedTenants = this.tenantsService.getStoredTenants().sort((a, b) => a.tenantId.localeCompare(b.tenantId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
|
import { ModuleWithProviders, NgModule } from '@angular/core';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
import { CircleButtonComponent, IconButtonComponent } from '../buttons';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { TenantSelectComponent } from './tenant-select/tenant-select.component';
|
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { RouterLink } from '@angular/router';
|
||||||
import { TenantIdInterceptor, TenantIdResponseInterceptor } from './services';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
import { CircleButtonComponent, IconButtonComponent } from '../buttons';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { StopPropagationDirective } from '../directives';
|
||||||
import { LogoComponent } from '../shared';
|
import { LogoComponent } from '../shared';
|
||||||
import { SpacerComponent } from '../shared/spacer/spacer.component';
|
import { SpacerComponent } from '../shared/spacer/spacer.component';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { TenantIdInterceptor, TenantIdResponseInterceptor } from './services';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { TenantSelectComponent } from './tenant-select/tenant-select.component';
|
||||||
import { RouterLink } from '@angular/router';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [TenantSelectComponent],
|
declarations: [TenantSelectComponent],
|
||||||
@ -33,6 +34,7 @@ import { RouterLink } from '@angular/router';
|
|||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
|
StopPropagationDirective,
|
||||||
],
|
],
|
||||||
exports: [TenantSelectComponent],
|
exports: [TenantSelectComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user