Compare commits

...

6 Commits
prod ... master

Author SHA1 Message Date
Kyryll O. e44203c485 fix: emit() publishes to exchange instead of sendToQueue
NestJS v10 RMQ client uses sendToQueue which sends to a specific
queue (core_queue), bypassing the exchange. Messages were never
delivered to other plugins' queues bound to the exchange.

Changed emit() to use amqplib to publish directly to system_events
exchange with {pattern, data} format that NestJS RMQ server expects.
2026-06-19 16:17:47 +03:00
Kyryll O. fb0bb36bdd fix: publish system.notification.send in {pattern, data} format for NestJS RMQ 2026-06-18 16:45:12 +03:00
Kyryll O. 8209f4ac82 fix: sendNotification publishes directly to exchange instead of NestJS RMQ client sendToQueue 2026-06-18 16:32:01 +03:00
Kyryll O. 67f58d6f74 1.11.0 2026-06-12 17:55:18 +03:00
Kyryll O. 3822e91243 chore: bump to v1.10.3 (callPluginApi unwrap response) 2026-06-12 17:54:57 +03:00
Kyryll O. de45ca428b fix: callPluginApi unwrap response (TransformInterceptor wrapping) 2026-06-12 17:53:48 +03:00
3 changed files with 44 additions and 15 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@erp/plugin-sdk", "name": "@erp/plugin-sdk",
"version": "1.10.1", "version": "1.11.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@erp/plugin-sdk", "name": "@erp/plugin-sdk",
"version": "1.10.1", "version": "1.11.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"uuid": "^9.0.1" "uuid": "^9.0.1"

View File

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

View File

@ -162,10 +162,17 @@ export class CoreService {
} }
async callPluginApi<T = any>(pluginId: string, path: string, body?: any): Promise<T> { async callPluginApi<T = any>(pluginId: string, path: string, body?: any): Promise<T> {
return this.fetchCore(`/api/plugins/rpc/${pluginId}/${path.replace(/^\//, '')}`, { const res = await this.fetchCore(`/api/plugins/rpc/${pluginId}/${path.replace(/^\//, '')}`, {
method: 'POST', method: 'POST',
body: body !== undefined ? JSON.stringify(body) : undefined, body: body !== undefined ? JSON.stringify(body) : undefined,
}); });
if (res && typeof res === 'object' && res.success === true) {
if (res.data && typeof res.data === 'object' && res.data.success === true) {
return res.data.data as T;
}
return res.data as T;
}
return res as T;
} }
async callAsService<T = any>(pluginId: string, path: string, body?: any): Promise<T> { async callAsService<T = any>(pluginId: string, path: string, body?: any): Promise<T> {
@ -324,9 +331,19 @@ export class CoreService {
source: this.serviceName, source: this.serviceName,
timestamp: Date.now(), timestamp: Date.now(),
}; };
this.client.emit(pattern, event).subscribe({ try {
error: (err) => this.logger.error(`Failed to emit event ${pattern}:`, err), const conn = await amqp.connect(this.getRMQUrl());
}); const ch = await conn.createChannel();
await ch.assertExchange('system_events', 'topic', { durable: true });
ch.publish('system_events', pattern, Buffer.from(JSON.stringify({
pattern,
data: event,
})));
await ch.close();
await conn.close();
} catch (err: any) {
this.logger.error(`Failed to emit event ${pattern}: ${err.message}`);
}
} }
async auditLog(action: string, resource: string, details?: any): Promise<void> { async auditLog(action: string, resource: string, details?: any): Promise<void> {
@ -399,14 +416,26 @@ export class CoreService {
} }
async sendNotification(payload: { userId: number | null; title: string; message: string; icon?: string; image?: string; details?: any }): Promise<void> { async sendNotification(payload: { userId: number | null; title: string; message: string; icon?: string; image?: string; details?: any }): Promise<void> {
await this.emit('system.notification.send', { try {
userId: payload.userId, const conn = await amqp.connect(this.getRMQUrl());
title: payload.title, const ch = await conn.createChannel();
message: payload.message, await ch.assertExchange('system_events', 'topic', { durable: true });
icon: payload.icon, ch.publish('system_events', 'system.notification.send', Buffer.from(JSON.stringify({
image: payload.image, pattern: 'system.notification.send',
details: payload.details, data: {
}); userId: payload.userId,
title: payload.title,
message: payload.message,
icon: payload.icon,
image: payload.image,
details: payload.details,
},
})));
await ch.close();
await conn.close();
} catch (err: any) {
this.logger.error(`[sendNotification] Failed: ${err.message}`);
}
} }
getDbName(): string { getDbName(): string {