common-ui/src/lib/tenants/services/tenants.service.ts

122 lines
4.3 KiB
TypeScript

import { inject, Injectable, signal } from '@angular/core';
import { NGXLogger } from 'ngx-logger';
import { List } from '../../utils';
import dayjs from 'dayjs';
export interface IStoredTenantId {
readonly tenantId: string;
readonly email: string;
readonly created: string;
}
export type StoredTenantIds = List<IStoredTenantId>;
const STORED_TENANTS_KEY = 'red-stored-tenants';
@Injectable({ providedIn: 'root' })
export class TenantsService {
readonly #logger = inject(NGXLogger);
readonly #storageReference: Storage = {
length: localStorage.length,
clear: localStorage.clear.bind(localStorage),
getItem: localStorage.getItem.bind(localStorage),
setItem: localStorage.setItem.bind(localStorage),
removeItem: localStorage.removeItem.bind(localStorage),
key: localStorage.key.bind(localStorage),
};
readonly #activeTenantId = signal<string>('');
protected readonly _serviceName: string = 'tenant-user-management';
get activeTenantId() {
return this.#activeTenantId();
}
async selectTenant(tenantId: string) {
this.#mutateStorage(tenantId);
this.#setActiveTenantId(tenantId);
return true;
}
storeTenant(emailOrUsername: string) {
if (!emailOrUsername) {
this.#logger.warn('[TENANTS] Email or username is null, skip storing');
return;
}
const storedTenants = this.getStoredTenants();
const activeTenantId = this.#activeTenantId();
const existing = storedTenants.find(s => s.email === emailOrUsername && s.tenantId === activeTenantId);
if (existing) {
this.#logger.info('[TENANTS] Stored tenant exists: ', storedTenants);
return;
}
if (!activeTenantId) {
this.#logger.error('[TENANTS] Active tenant is null when storing tenants');
return;
}
storedTenants.push({ tenantId: activeTenantId, email: emailOrUsername, created: new Date().toISOString() });
this.#storageReference.setItem(STORED_TENANTS_KEY, JSON.stringify(storedTenants));
this.#logger.info('[TENANTS] Stored tenants: ', storedTenants);
}
getStoredTenants() {
const rawStoredTenants = this.#storageReference.getItem(STORED_TENANTS_KEY);
const stored = JSON.parse(rawStoredTenants ?? '[]') as StoredTenantIds;
const validStoredTenants: IStoredTenantId[] = [];
for (const s of stored) {
const date1 = dayjs(s.created);
const date2 = dayjs();
const diff = date2.diff(date1, 'days');
const is90DaysOld = diff >= 90;
if (is90DaysOld) {
this.#logger.warn(`[TENANTS] Saved tenant ${s.tenantId} - ${s.email} is 90 days old, delete it`);
this.removeStored(s.email);
continue;
}
validStoredTenants.push(s);
}
return validStoredTenants;
}
removeStored(email: string) {
if (!email) {
this.#logger.warn('[TENANTS] Email is null, skip storing');
return;
}
const storedTenants = this.getStoredTenants();
const activeTenantId = this.#activeTenantId();
const existing = storedTenants.find(s => s.email === email && s.tenantId === activeTenantId);
if (!existing) {
this.#logger.info('[TENANTS] No stored tenant for ', email);
return;
}
const serializableTenants = storedTenants.filter(s => s !== existing);
this.#storageReference.setItem(STORED_TENANTS_KEY, JSON.stringify(serializableTenants));
this.#logger.info('[TENANTS] Stored tenants at logout: ', storedTenants);
}
#setActiveTenantId(tenantId: string) {
this.#logger.info('[TENANTS] Set current tenant id: ', tenantId);
this.#activeTenantId.set(tenantId);
}
#mutateStorage(tenant: string) {
localStorage.getItem = (key: string) => {
return this.#storageReference.getItem(tenant + ':' + key);
};
localStorage.setItem = (key: string, value: string) => {
this.#storageReference.setItem(tenant + ':' + key, value);
};
localStorage.removeItem = (key: string) => {
this.#storageReference.removeItem(tenant + ':' + key);
};
}
}