common-ui/src/lib/tenants/services/tenants.service.ts
2023-05-18 12:19:53 +03:00

113 lines
4.0 KiB
TypeScript

import { computed, inject, Injectable, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
import { NGXLogger } from 'ngx-logger';
import { BASE_HREF } from '../../utils';
import { Router } from '@angular/router';
export interface IBaseTenant {
readonly tenantId: string;
readonly displayName: string;
readonly guid: string;
}
@Injectable({ providedIn: 'root' })
export class TenantsService {
readonly tenants = signal<IBaseTenant[] | undefined>(undefined);
readonly hasMultiple = computed(() => {
const tenants = this.tenants();
return tenants ? tenants.length > 1 : false;
});
readonly #http = inject(HttpClient);
readonly #router = inject(Router);
readonly #logger = inject(NGXLogger);
readonly #baseHref = inject(BASE_HREF);
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>('');
get currentTenant() {
return this.#activeTenantId();
}
async loadTenants() {
this.#logger.info('[TENANTS] Loading tenants...');
const tenants = await firstValueFrom(this.#http.get<IBaseTenant[]>('/tenants/simple'));
this.tenants.set(tenants);
const tenant = this.getTenantFromRoute();
if (tenant) {
this.#logger.info('[TENANTS] Tenant from route: ', tenant);
if (await this.selectTenant(tenant)) {
await this.#executeMainResolverAndRedirect(tenant);
}
return;
}
this.#logger.info('[TENANTS] No tenant in route');
if (!this.hasMultiple) {
this.#logger.info('[TENANTS] Only one tenant loaded, auto-select it and redirect to login page');
const tenant = tenants[0].tenantId;
if (await this.selectTenant(tenant)) {
await this.#executeMainResolverAndRedirect(tenant);
}
}
}
getTenantFromRoute() {
const path = window.location.pathname;
const nextSlash = path.indexOf('/', this.#baseHref.length + 1);
return path.substring(this.#baseHref.length + 1, nextSlash >= 0 ? nextSlash : path.length);
}
async selectTenant(tenantId: string) {
const tenants = this.tenants();
if (!tenants) {
throw new Error('Tenants not loaded!');
}
const unknownTenant = !tenants.map(t => t.tenantId).includes(tenantId);
if (unknownTenant) {
this.#logger.info('[TENANTS] Unknown tenant, redirecting to select tenant page');
await this.#router.navigate(['/']);
return false;
}
this.#mutateStorage(tenantId);
this.#setCurrentTenantId(tenantId);
return true;
}
#setCurrentTenantId(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);
};
}
async #executeMainResolverAndRedirect(tenant: string) {
const intendedPath = window.location.pathname.replace(this.#baseHref, '');
// TODO: Load roles in child routes canLoad functions so we can redirect straight to wanted URL
await this.#router.navigate([tenant], { skipLocationChange: true });
await this.#router.navigate([intendedPath]);
}
}