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:
parent
b4d214d9c3
commit
3451a23e8e
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@erp/plugin-sdk",
|
"name": "@erp/plugin-sdk",
|
||||||
"version": "1.10.1",
|
"version": "1.10.2",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { ClientProxy } from '@nestjs/microservices';
|
||||||
import { firstValueFrom, timeout as rxTimeout } from 'rxjs';
|
import { firstValueFrom, timeout as rxTimeout } from 'rxjs';
|
||||||
import { SystemEvent } from '../interfaces/communication';
|
import { SystemEvent } from '../interfaces/communication';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as amqp from 'amqplib';
|
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[]> {
|
async discoverPlugins(): Promise<any[]> {
|
||||||
try {
|
try {
|
||||||
const response = await this.fetchCore('/api/plugins/discovery');
|
const response = await this.fetchCore('/api/plugins/discovery');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue