@onlineapps/service-wrapper 2.1.89 → 2.1.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,11 +19,6 @@
19
19
  "enabled": true,
20
20
  "metrics": ["requests", "errors", "duration"]
21
21
  },
22
- "startupAlerts": {
23
- "enabled": true,
24
- "cooldownMs": 600000,
25
- "stateFile": "/tmp/oa_startup_failure_state.json"
26
- },
27
22
  "logging": {
28
23
  "enabled": true,
29
24
  "level": "info",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/service-wrapper",
3
- "version": "2.1.89",
3
+ "version": "2.1.90",
4
4
  "description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -29,9 +29,9 @@
29
29
  "@onlineapps/conn-infra-error-handler": "1.0.8",
30
30
  "@onlineapps/conn-infra-mq": "1.1.66",
31
31
  "@onlineapps/conn-orch-api-mapper": "1.0.26",
32
- "@onlineapps/conn-orch-cookbook": "2.0.27",
33
- "@onlineapps/conn-orch-orchestrator": "1.0.91",
34
- "@onlineapps/conn-orch-registry": "1.1.43",
32
+ "@onlineapps/conn-orch-cookbook": "2.0.28",
33
+ "@onlineapps/conn-orch-orchestrator": "1.0.92",
34
+ "@onlineapps/conn-orch-registry": "1.1.44",
35
35
  "@onlineapps/conn-orch-validator": "2.0.25",
36
36
  "@onlineapps/monitoring-core": "1.0.20",
37
37
  "@onlineapps/service-common": "1.0.15",
@@ -417,20 +417,16 @@ class ServiceWrapper {
417
417
  /**
418
418
  * Handle initialization error with restart strategy
419
419
  * @private
420
+ *
421
+ * NOTE: Alerting is NOT done here. Before registration, service is in "dev care" phase.
422
+ * After registration, Registry monitors heartbeats and triggers alerts via Monitoring.
423
+ * This keeps alerting responsibility centralized in Monitoring (Separation of Concerns).
420
424
  */
421
425
  async _handleInitializationError(phase, phaseName, error, isTransient = false) {
422
426
  this._logPhase(phase, phaseName, 'FAILED', error);
423
427
 
424
428
  // Cleanup before restart
425
429
  await this._cleanupBeforeRestart();
426
-
427
- // Startup failure alert (works without MQ/monitoring)
428
- try {
429
- await this._maybeSendStartupFailureAlert({ phase, phaseName, error, isTransient });
430
- } catch (alertErr) {
431
- // Alerts must never block restart; log and continue.
432
- console.warn('[StartupAlerts] Failed to send startup failure alert:', alertErr.message);
433
- }
434
430
 
435
431
  if (isTransient) {
436
432
  // Přechodná chyba → restart může pomoci
@@ -443,81 +439,6 @@ class ServiceWrapper {
443
439
  }
444
440
  }
445
441
 
446
- /**
447
- * Send startup-failure alert via SMTP (independent of MQ).
448
- * Uses service-common sendMonitoringFailFallbackEmail (SMTP config via INFRA_REPORT_* env).
449
- * Throttled by a persistent state file (survives container restarts).
450
- *
451
- * @private
452
- */
453
- async _maybeSendStartupFailureAlert({ phase, phaseName, error, isTransient }) {
454
- const cfg = this.config.wrapper?.startupAlerts;
455
- if (!cfg || cfg.enabled !== true) {
456
- return;
457
- }
458
-
459
- const cooldownMs = cfg.cooldownMs;
460
- const stateFile = cfg.stateFile;
461
- if (typeof cooldownMs !== 'number' || Number.isNaN(cooldownMs) || cooldownMs <= 0) {
462
- throw new Error(`[StartupAlerts] Invalid configuration - wrapper.startupAlerts.cooldownMs must be a positive number, got: ${cooldownMs}`);
463
- }
464
- if (typeof stateFile !== 'string' || stateFile.trim() === '') {
465
- throw new Error(`[StartupAlerts] Invalid configuration - wrapper.startupAlerts.stateFile must be a non-empty string, got: ${stateFile}`);
466
- }
467
-
468
- const fs = require('fs');
469
- const path = require('path');
470
- const serviceName = this.config.service?.name || 'unnamed-service';
471
- const now = Date.now();
472
-
473
- let state = { lastAlertAt: 0, failureCount: 0, lastFailureAt: 0 };
474
- try {
475
- if (fs.existsSync(stateFile)) {
476
- const raw = fs.readFileSync(stateFile, 'utf8');
477
- state = { ...state, ...(JSON.parse(raw) || {}) };
478
- } else {
479
- // Ensure parent dir exists (best-effort)
480
- const dir = path.dirname(stateFile);
481
- try { fs.mkdirSync(dir, { recursive: true }); } catch (_) { /* ignore */ }
482
- }
483
- } catch (e) {
484
- // If state cannot be read, continue with defaults (alerting must not crash init)
485
- }
486
-
487
- state.failureCount = (state.failureCount || 0) + 1;
488
- state.lastFailureAt = now;
489
-
490
- const shouldSend = !state.lastAlertAt || (now - state.lastAlertAt) >= cooldownMs;
491
- if (!shouldSend) {
492
- // Persist updated counters anyway
493
- try { fs.writeFileSync(stateFile, JSON.stringify(state), 'utf8'); } catch (_) { /* ignore */ }
494
- return;
495
- }
496
-
497
- const { sendMonitoringFailFallbackEmail } = require('@onlineapps/service-common');
498
- const transientLabel = isTransient ? 'TRANSIENT' : 'PERMANENT';
499
- const subject = `[StartupFailure] ${serviceName} phase ${phase} (${transientLabel})`;
500
- const text = [
501
- `Service: ${serviceName}`,
502
- `Phase: ${phase} (${phaseName})`,
503
- `Type: ${transientLabel}`,
504
- `Failure count (container): ${state.failureCount}`,
505
- `Timestamp: ${new Date(now).toISOString()}`,
506
- `Error: ${error?.message || 'unknown error'}`
507
- ].join('\n');
508
- const html = `<pre>${text}</pre>`;
509
-
510
- const sent = await sendMonitoringFailFallbackEmail(subject, text, html);
511
- state.lastAlertAt = now;
512
- try { fs.writeFileSync(stateFile, JSON.stringify(state), 'utf8'); } catch (_) { /* ignore */ }
513
-
514
- if (sent) {
515
- this.logger?.info('[StartupAlerts] ✓ Startup failure alert sent', { service: serviceName, phase, transient: isTransient });
516
- } else {
517
- this.logger?.warn('[StartupAlerts] Startup failure alert not sent (SMTP config missing or send failed)', { service: serviceName, phase });
518
- }
519
- }
520
-
521
442
  async initialize() {
522
443
  if (this.isInitialized) {
523
444
  // Logger might not be initialized yet, use console as fallback
@@ -911,6 +832,7 @@ class ServiceWrapper {
911
832
  amqpUrl: mqUrl,
912
833
  serviceName: serviceName,
913
834
  version: this.config.service?.version,
835
+ specificationEndpoint: this.config.service?.specificationEndpoint || '/api/v1/specification',
914
836
  registryQueue: 'registry.register', // Use correct queue name
915
837
  registryUrl: registryUrl,
916
838
  logger: this.logger || console,