94 lines
2.5 KiB
TypeScript
94 lines
2.5 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
|
|
import type { WhmcsRegisterMtlsRequest } from '@hexahost/contracts';
|
|
import { normalizeCertificateFingerprint } from '@hexahost/integration-auth';
|
|
|
|
import { PrismaService } from '../../prisma/prisma.service';
|
|
|
|
type InstallationRef = {
|
|
id: string;
|
|
};
|
|
|
|
@Injectable()
|
|
export class WhmcsMtlsService {
|
|
constructor(private readonly prisma: PrismaService) {}
|
|
|
|
async registerFingerprint(
|
|
installation: InstallationRef,
|
|
input: WhmcsRegisterMtlsRequest,
|
|
idempotencyKey: string,
|
|
): Promise<{
|
|
integrationId: string;
|
|
fingerprint: string | null;
|
|
subject: string | null;
|
|
registered: boolean;
|
|
}> {
|
|
const existingOp = await this.prisma.integrationOperation.findUnique({
|
|
where: { idempotencyKey },
|
|
});
|
|
if (existingOp?.result) {
|
|
return existingOp.result as {
|
|
integrationId: string;
|
|
fingerprint: string | null;
|
|
subject: string | null;
|
|
registered: boolean;
|
|
};
|
|
}
|
|
|
|
const fingerprint = normalizeCertificateFingerprint(input.fingerprint);
|
|
|
|
const updated = await this.prisma.whmcsInstallation.update({
|
|
where: { id: installation.id },
|
|
data: {
|
|
mtlsClientFingerprint: fingerprint,
|
|
mtlsClientSubject: input.subject ?? null,
|
|
},
|
|
select: {
|
|
integrationId: true,
|
|
mtlsClientFingerprint: true,
|
|
mtlsClientSubject: true,
|
|
},
|
|
});
|
|
|
|
const result = {
|
|
integrationId: updated.integrationId,
|
|
fingerprint: updated.mtlsClientFingerprint,
|
|
subject: updated.mtlsClientSubject,
|
|
registered: true,
|
|
};
|
|
|
|
await this.prisma.integrationOperation.create({
|
|
data: {
|
|
installationId: installation.id,
|
|
idempotencyKey,
|
|
operation: 'mtls:register',
|
|
status: 'COMPLETED',
|
|
result: result as object,
|
|
},
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
async getMtlsStatus(installation: InstallationRef) {
|
|
const row = await this.prisma.whmcsInstallation.findUniqueOrThrow({
|
|
where: { id: installation.id },
|
|
select: {
|
|
integrationId: true,
|
|
mtlsClientFingerprint: true,
|
|
mtlsClientSubject: true,
|
|
},
|
|
});
|
|
|
|
return {
|
|
integrationId: row.integrationId,
|
|
mtlsEnabled: process.env['INTEGRATION_MTLS_ENABLED'] === 'true',
|
|
fingerprintRegistered: Boolean(row.mtlsClientFingerprint),
|
|
fingerprintPreview: row.mtlsClientFingerprint
|
|
? `${row.mtlsClientFingerprint.slice(0, 8)}…${row.mtlsClientFingerprint.slice(-8)}`
|
|
: null,
|
|
subject: row.mtlsClientSubject,
|
|
};
|
|
}
|
|
}
|