@sourceloop/ctrl-plane-tenant-management-service
Version:
Tenant Management microservice for SaaS control plane
89 lines (81 loc) • 3.23 kB
text/typescript
import {repository} from '@loopback/repository';
import {SubscriptionDTO, TenantWithRelations} from '../models';
import {CryptoHelperService} from './crypto-helper.service';
import {service, inject} from '@loopback/core';
import {TenantRepository, WebhookSecretRepository} from '../repositories';
import {HttpErrors} from '@loopback/rest';
import {IProvisioningService} from '../types';
import {ILogger, LOGGER} from '@sourceloop/core';
import {TenantStatus} from '../enums';
import {EventConnectorBinding} from '../keys';
import {EventConnector, EventTypes} from './event-connector';
import {randomUUID} from 'crypto';
/**
* Service for provisioning tenants.
*/
export class ProvisioningService<T extends SubscriptionDTO>
implements IProvisioningService<T>
{
/**
* Constructs a new instance of the ProvisioningService.
* @param cryptoHelperService - Service for cryptographic operations.
* @param eventConnector - Event Connector provided by consumer to invoke publish on.
* @param webhookSecretRepo - Repository for webhook secrets.
* @param logger - Logger service for logging messages.
*/
constructor(
(CryptoHelperService)
private cryptoHelperService: CryptoHelperService,
(EventConnectorBinding)
private eventConnector: EventConnector,
(WebhookSecretRepository)
private webhookSecretRepo: WebhookSecretRepository,
(TenantRepository)
private tenantRepository: TenantRepository,
(LOGGER.LOGGER_INJECT)
private logger: ILogger,
) {}
/**
* The `provisionTenant` function provisions a new tenant by generating a temporary token, retrieving
* the subscription details, generating a random HMAC secret, starting a build process, and storing the
* webhook secret and build context to later verify the webhook callback.
* @param {Tenant} tenant - The `tenant` parameter is an object that represents a tenant. It likely
* contains information such as the tenant's ID, name, and other relevant details.
* @param {string} subscription - The `subscription` parameter is an object that contains
* the details of the subscription opted by the tenant.
*/
async provisionTenant(
tenant: TenantWithRelations,
subscription: T,
): Promise<void> {
await this.tenantRepository.updateById(tenant.id, {
status: TenantStatus.PROVISIONING,
});
if (!subscription.id) {
throw HttpErrors.BadRequest('Subscription ID is required');
}
const hmacSecret = this.cryptoHelperService.generateRandomString(32);
if (subscription.plan?.tier === undefined) {
this.logger.error(
`Tier not found in plan for subscription: ${subscription.id}`,
);
throw new HttpErrors.InternalServerError();
}
const context = randomUUID();
await this.eventConnector.publish({
type: EventTypes.TENANT_PROVISIONING,
tenant: tenant,
subscription: subscription,
secret: hmacSecret,
context: context,
});
await this.webhookSecretRepo.set(tenant.id, {
secret: hmacSecret,
context: context,
});
await this.webhookSecretRepo.expire(
tenant.id,
+process.env.WEBHOOK_SECRET_EXPIRY!,
);
}
}