156 lines
4.4 KiB
TypeScript
156 lines
4.4 KiB
TypeScript
import {
|
|
Controller,
|
|
Get,
|
|
Put,
|
|
Body,
|
|
UseGuards,
|
|
Req,
|
|
} from '@nestjs/common';
|
|
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
|
import { TenantDatabaseService } from './tenant-database.service';
|
|
import { getCentralPrisma } from '../prisma/central-prisma.service';
|
|
import { TenantId } from './tenant.decorator';
|
|
|
|
@Controller('tenant')
|
|
@UseGuards(JwtAuthGuard)
|
|
export class TenantController {
|
|
constructor(private readonly tenantDbService: TenantDatabaseService) {}
|
|
|
|
/**
|
|
* Get integrations configuration for the current tenant
|
|
*/
|
|
@Get('integrations')
|
|
async getIntegrationsConfig(@TenantId() domain: string) {
|
|
const centralPrisma = getCentralPrisma();
|
|
|
|
// Look up tenant by domain
|
|
const domainRecord = await centralPrisma.domain.findUnique({
|
|
where: { domain },
|
|
include: { tenant: { select: { id: true, integrationsConfig: true } } },
|
|
});
|
|
|
|
if (!domainRecord?.tenant || !domainRecord.tenant.integrationsConfig) {
|
|
return { data: null };
|
|
}
|
|
|
|
// Decrypt the config
|
|
const config = this.tenantDbService.decryptIntegrationsConfig(
|
|
domainRecord.tenant.integrationsConfig as any,
|
|
);
|
|
|
|
// Return config with sensitive fields masked
|
|
const maskedConfig = this.maskSensitiveFields(config);
|
|
|
|
return { data: maskedConfig };
|
|
}
|
|
|
|
/**
|
|
* Update integrations configuration for the current tenant
|
|
*/
|
|
@Put('integrations')
|
|
async updateIntegrationsConfig(
|
|
@TenantId() domain: string,
|
|
@Body() body: { integrationsConfig: any },
|
|
) {
|
|
const { integrationsConfig } = body;
|
|
|
|
if (!domain) {
|
|
throw new Error('Domain is missing from request');
|
|
}
|
|
|
|
// Look up tenant by domain
|
|
const centralPrisma = getCentralPrisma();
|
|
const domainRecord = await centralPrisma.domain.findUnique({
|
|
where: { domain },
|
|
include: { tenant: { select: { id: true, integrationsConfig: true } } },
|
|
});
|
|
|
|
if (!domainRecord?.tenant) {
|
|
throw new Error(`Tenant with domain ${domain} not found`);
|
|
}
|
|
|
|
// Merge with existing config to preserve masked values
|
|
let finalConfig = integrationsConfig;
|
|
if (domainRecord.tenant.integrationsConfig) {
|
|
const existingConfig = this.tenantDbService.decryptIntegrationsConfig(
|
|
domainRecord.tenant.integrationsConfig as any,
|
|
);
|
|
|
|
// Replace masked values with actual values from existing config
|
|
finalConfig = this.unmaskConfig(integrationsConfig, existingConfig);
|
|
}
|
|
|
|
// Encrypt the config
|
|
const encryptedConfig = this.tenantDbService.encryptIntegrationsConfig(
|
|
finalConfig,
|
|
);
|
|
|
|
// Update in database
|
|
await centralPrisma.tenant.update({
|
|
where: { id: domainRecord.tenant.id },
|
|
data: {
|
|
integrationsConfig: encryptedConfig as any,
|
|
},
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
message: 'Integrations configuration updated successfully',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Unmask config by replacing masked values with actual values from existing config
|
|
*/
|
|
private unmaskConfig(newConfig: any, existingConfig: any): any {
|
|
const result = { ...newConfig };
|
|
|
|
// Unmask Twilio credentials
|
|
if (result.twilio && existingConfig.twilio) {
|
|
if (result.twilio.authToken === '••••••••' && existingConfig.twilio.authToken) {
|
|
result.twilio.authToken = existingConfig.twilio.authToken;
|
|
}
|
|
if (result.twilio.apiSecret === '••••••••' && existingConfig.twilio.apiSecret) {
|
|
result.twilio.apiSecret = existingConfig.twilio.apiSecret;
|
|
}
|
|
}
|
|
|
|
// Unmask OpenAI credentials
|
|
if (result.openai && existingConfig.openai) {
|
|
if (result.openai.apiKey === '••••••••' && existingConfig.openai.apiKey) {
|
|
result.openai.apiKey = existingConfig.openai.apiKey;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Mask sensitive fields for API responses
|
|
*/
|
|
private maskSensitiveFields(config: any): any {
|
|
if (!config) return null;
|
|
|
|
const masked = { ...config };
|
|
|
|
// Mask Twilio credentials
|
|
if (masked.twilio) {
|
|
masked.twilio = {
|
|
...masked.twilio,
|
|
authToken: masked.twilio.authToken ? '••••••••' : '',
|
|
apiSecret: masked.twilio.apiSecret ? '••••••••' : '',
|
|
};
|
|
}
|
|
|
|
// Mask OpenAI credentials
|
|
if (masked.openai) {
|
|
masked.openai = {
|
|
...masked.openai,
|
|
apiKey: masked.openai.apiKey ? '••••••••' : '',
|
|
};
|
|
}
|
|
|
|
return masked;
|
|
}
|
|
}
|