feat: add callAsService for service-to-service auth

- Add CoreService.callAsService(pluginId, path, body) method
- Signs requests with HMAC-SHA256 using REGISTRATION_KEY
- Sends X-Service-Auth header for service-to-service authentication
This commit is contained in:
Kyryll O. 2026-06-04 13:28:50 +03:00
parent b4d214d9c3
commit 3451a23e8e
2 changed files with 31 additions and 1 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@erp/plugin-sdk",
"version": "1.10.1",
"version": "1.10.2",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {

View File

@ -3,6 +3,7 @@ import { ClientProxy } from '@nestjs/microservices';
import { firstValueFrom, timeout as rxTimeout } from 'rxjs';
import { SystemEvent } from '../interfaces/communication';
import { v4 as uuidv4 } from 'uuid';
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import * as amqp from 'amqplib';
@ -167,6 +168,35 @@ export class CoreService {
});
}
async callAsService<T = any>(pluginId: string, path: string, body?: any): Promise<T> {
const registrationKey = process.env.REGISTRATION_KEY || '';
if (!registrationKey) {
throw new Error('REGISTRATION_KEY not configured — cannot make service-to-service call');
}
const payload = JSON.stringify({
pluginId,
path: path.replace(/^\//, ''),
timestamp: Date.now(),
body: body !== undefined ? body : null,
});
const signature = crypto.createHmac('sha256', registrationKey).update(payload).digest('hex');
const encoded = Buffer.from(payload).toString('base64');
const serviceAuth = `v1.${encoded}.${signature}`;
const coreUrl = process.env.CORE_BACKEND_URL || 'http://localhost:3001';
const response = await fetch(`${coreUrl}/api/plugins/rpc/${pluginId}/${path.replace(/^\//, '')}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Service-Auth': serviceAuth },
body: body !== undefined ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
const text = await response.text().catch(() => response.statusText);
throw new Error(`Service call to ${pluginId}/${path} failed: ${response.status} ${text}`);
}
const text = await response.text();
try { return JSON.parse(text); } catch { return text as any; }
}
async discoverPlugins(): Promise<any[]> {
try {
const response = await this.fetchCore('/api/plugins/discovery');