@serve.zone/dcrouter 17.2.4 → 17.4.0

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.
Files changed (113) hide show
  1. package/deno.json +1 -1
  2. package/dist_serve/bundle.js +2024 -1758
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/classes.dcrouter.d.ts +11 -1
  5. package/dist_ts/classes.dcrouter.js +63 -3
  6. package/dist_ts/config/classes.api-token-manager.d.ts +20 -2
  7. package/dist_ts/config/classes.api-token-manager.js +201 -6
  8. package/dist_ts/config/classes.gateway-client-manager.d.ts +36 -2
  9. package/dist_ts/config/classes.gateway-client-manager.js +391 -27
  10. package/dist_ts/db/documents/classes.api-token.doc.d.ts +2 -1
  11. package/dist_ts/db/documents/classes.api-token.doc.js +8 -2
  12. package/dist_ts/db/documents/classes.cached.email.d.ts +10 -1
  13. package/dist_ts/db/documents/classes.cached.email.js +27 -2
  14. package/dist_ts/db/documents/classes.gateway-client.doc.d.ts +25 -0
  15. package/dist_ts/db/documents/classes.gateway-client.doc.js +73 -2
  16. package/dist_ts/db/documents/classes.ops-config-event.doc.d.ts +55 -0
  17. package/dist_ts/db/documents/classes.ops-config-event.doc.js +296 -0
  18. package/dist_ts/db/documents/index.d.ts +1 -0
  19. package/dist_ts/db/documents/index.js +2 -1
  20. package/dist_ts/dns/classes.gateway-route-dns-reconciler.d.ts +52 -0
  21. package/dist_ts/dns/classes.gateway-route-dns-reconciler.js +609 -0
  22. package/dist_ts/dns/index.d.ts +1 -0
  23. package/dist_ts/dns/index.js +2 -1
  24. package/dist_ts/dns/manager.dns.d.ts +19 -0
  25. package/dist_ts/dns/manager.dns.js +25 -1
  26. package/dist_ts/email/classes.accepted-email-spool.d.ts +8 -1
  27. package/dist_ts/email/classes.accepted-email-spool.js +93 -1
  28. package/dist_ts/email/classes.smartmta-storage-manager.d.ts +7 -1
  29. package/dist_ts/email/classes.smartmta-storage-manager.js +46 -1
  30. package/dist_ts/email/smartmta-storage-policy.js +3 -1
  31. package/dist_ts/monitoring/classes.ops-event-manager.d.ts +32 -0
  32. package/dist_ts/monitoring/classes.ops-event-manager.js +71 -0
  33. package/dist_ts/monitoring/index.d.ts +1 -0
  34. package/dist_ts/monitoring/index.js +2 -1
  35. package/dist_ts/opsserver/classes.opsserver.d.ts +1 -0
  36. package/dist_ts/opsserver/classes.opsserver.js +3 -1
  37. package/dist_ts/opsserver/handlers/certificate.handler.d.ts +1 -0
  38. package/dist_ts/opsserver/handlers/certificate.handler.js +35 -5
  39. package/dist_ts/opsserver/handlers/config-event.handler.d.ts +12 -0
  40. package/dist_ts/opsserver/handlers/config-event.handler.js +47 -0
  41. package/dist_ts/opsserver/handlers/dns-record.handler.d.ts +1 -0
  42. package/dist_ts/opsserver/handlers/dns-record.handler.js +82 -3
  43. package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +6 -0
  44. package/dist_ts/opsserver/handlers/email-ops.handler.js +40 -9
  45. package/dist_ts/opsserver/handlers/gatewayclient.handler.d.ts +9 -1
  46. package/dist_ts/opsserver/handlers/gatewayclient.handler.js +714 -162
  47. package/dist_ts/opsserver/handlers/index.d.ts +1 -0
  48. package/dist_ts/opsserver/handlers/index.js +2 -1
  49. package/dist_ts/opsserver/helpers/gateway-client-auth.d.ts +25 -0
  50. package/dist_ts/opsserver/helpers/gateway-client-auth.js +168 -0
  51. package/dist_ts/plugins.d.ts +2 -1
  52. package/dist_ts/plugins.js +3 -2
  53. package/dist_ts_apiclient/classes.gatewayclient.d.ts +13 -1
  54. package/dist_ts_apiclient/classes.gatewayclient.js +18 -2
  55. package/dist_ts_interfaces/data/config-events.d.ts +34 -0
  56. package/dist_ts_interfaces/data/config-events.js +8 -0
  57. package/dist_ts_interfaces/data/index.d.ts +1 -0
  58. package/dist_ts_interfaces/data/index.js +2 -1
  59. package/dist_ts_interfaces/data/route-management.d.ts +28 -1
  60. package/dist_ts_interfaces/data/route-management.js +3 -1
  61. package/dist_ts_interfaces/data/workhoster.d.ts +1 -0
  62. package/dist_ts_interfaces/requests/config-events.d.ts +35 -0
  63. package/dist_ts_interfaces/requests/config-events.js +4 -0
  64. package/dist_ts_interfaces/requests/index.d.ts +1 -0
  65. package/dist_ts_interfaces/requests/index.js +2 -1
  66. package/dist_ts_interfaces/requests/realtime.d.ts +1 -1
  67. package/dist_ts_interfaces/requests/realtime.js +2 -1
  68. package/dist_ts_migrations/index.js +148 -9
  69. package/dist_ts_web/00_commitinfo_data.js +1 -1
  70. package/dist_ts_web/appstate/config-events.d.ts +19 -0
  71. package/dist_ts_web/appstate/config-events.js +68 -0
  72. package/dist_ts_web/appstate/runtime.js +7 -1
  73. package/dist_ts_web/appstate.d.ts +1 -0
  74. package/dist_ts_web/appstate.js +2 -1
  75. package/dist_ts_web/elements/ops-platform-events.d.ts +21 -0
  76. package/dist_ts_web/elements/ops-platform-events.js +361 -0
  77. package/dist_ts_web/elements/ops-view-logs.d.ts +4 -0
  78. package/dist_ts_web/elements/ops-view-logs.js +96 -9
  79. package/package.json +4 -4
  80. package/readme.plan.md +22 -0
  81. package/ts/00_commitinfo_data.ts +1 -1
  82. package/ts/classes.dcrouter.ts +72 -2
  83. package/ts/config/classes.api-token-manager.ts +237 -4
  84. package/ts/config/classes.gateway-client-manager.ts +463 -19
  85. package/ts/db/documents/classes.api-token.doc.ts +8 -1
  86. package/ts/db/documents/classes.cached.email.ts +21 -1
  87. package/ts/db/documents/classes.gateway-client.doc.ts +86 -0
  88. package/ts/db/documents/classes.ops-config-event.doc.ts +257 -0
  89. package/ts/db/documents/index.ts +1 -0
  90. package/ts/dns/classes.gateway-route-dns-reconciler.ts +801 -0
  91. package/ts/dns/index.ts +1 -0
  92. package/ts/dns/manager.dns.ts +36 -0
  93. package/ts/email/classes.accepted-email-spool.ts +111 -0
  94. package/ts/email/classes.smartmta-storage-manager.ts +57 -1
  95. package/ts/email/smartmta-storage-policy.ts +2 -0
  96. package/ts/monitoring/classes.ops-event-manager.ts +99 -0
  97. package/ts/monitoring/index.ts +2 -1
  98. package/ts/opsserver/classes.opsserver.ts +2 -0
  99. package/ts/opsserver/handlers/certificate.handler.ts +51 -4
  100. package/ts/opsserver/handlers/config-event.handler.ts +62 -0
  101. package/ts/opsserver/handlers/dns-record.handler.ts +101 -2
  102. package/ts/opsserver/handlers/email-ops.handler.ts +42 -8
  103. package/ts/opsserver/handlers/gatewayclient.handler.ts +804 -136
  104. package/ts/opsserver/handlers/index.ts +1 -0
  105. package/ts/opsserver/helpers/gateway-client-auth.ts +198 -0
  106. package/ts/plugins.ts +2 -0
  107. package/ts_apiclient/classes.gatewayclient.ts +45 -0
  108. package/ts_web/00_commitinfo_data.ts +1 -1
  109. package/ts_web/appstate/config-events.ts +98 -0
  110. package/ts_web/appstate/runtime.ts +8 -1
  111. package/ts_web/appstate.ts +1 -0
  112. package/ts_web/elements/ops-platform-events.ts +315 -0
  113. package/ts_web/elements/ops-view-logs.ts +84 -7
@@ -34,6 +34,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
34
34
  };
35
35
  import * as shared from './shared/index.js';
36
36
  import * as appstate from '../appstate.js';
37
+ import './ops-platform-events.js';
37
38
  import { DeesElement, customElement, html, state, css, cssManager, } from '@design.estate/dees-element';
38
39
  let OpsViewLogs = (() => {
39
40
  let _classDecorators = [customElement('ops-view-logs')];
@@ -44,12 +45,22 @@ let OpsViewLogs = (() => {
44
45
  let _logState_decorators;
45
46
  let _logState_initializers = [];
46
47
  let _logState_extraInitializers = [];
48
+ let _activeTab_decorators;
49
+ let _activeTab_initializers = [];
50
+ let _activeTab_extraInitializers = [];
51
+ let _unacknowledgedCount_decorators;
52
+ let _unacknowledgedCount_initializers = [];
53
+ let _unacknowledgedCount_extraInitializers = [];
47
54
  var OpsViewLogs = class extends _classSuper {
48
55
  static { _classThis = this; }
49
56
  static {
50
57
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
51
58
  _logState_decorators = [state()];
59
+ _activeTab_decorators = [state()];
60
+ _unacknowledgedCount_decorators = [state()];
52
61
  __esDecorate(this, null, _logState_decorators, { kind: "accessor", name: "logState", static: false, private: false, access: { has: obj => "logState" in obj, get: obj => obj.logState, set: (obj, value) => { obj.logState = value; } }, metadata: _metadata }, _logState_initializers, _logState_extraInitializers);
62
+ __esDecorate(this, null, _activeTab_decorators, { kind: "accessor", name: "activeTab", static: false, private: false, access: { has: obj => "activeTab" in obj, get: obj => obj.activeTab, set: (obj, value) => { obj.activeTab = value; } }, metadata: _metadata }, _activeTab_initializers, _activeTab_extraInitializers);
63
+ __esDecorate(this, null, _unacknowledgedCount_decorators, { kind: "accessor", name: "unacknowledgedCount", static: false, private: false, access: { has: obj => "unacknowledgedCount" in obj, get: obj => obj.unacknowledgedCount, set: (obj, value) => { obj.unacknowledgedCount = value; } }, metadata: _metadata }, _unacknowledgedCount_initializers, _unacknowledgedCount_extraInitializers);
53
64
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
54
65
  OpsViewLogs = _classThis = _classDescriptor.value;
55
66
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -61,7 +72,13 @@ let OpsViewLogs = (() => {
61
72
  });
62
73
  get logState() { return this.#logState_accessor_storage; }
63
74
  set logState(value) { this.#logState_accessor_storage = value; }
64
- lastPushedCount = (__runInitializers(this, _logState_extraInitializers), 0);
75
+ #activeTab_accessor_storage = (__runInitializers(this, _logState_extraInitializers), __runInitializers(this, _activeTab_initializers, 'application'));
76
+ get activeTab() { return this.#activeTab_accessor_storage; }
77
+ set activeTab(value) { this.#activeTab_accessor_storage = value; }
78
+ #unacknowledgedCount_accessor_storage = (__runInitializers(this, _activeTab_extraInitializers), __runInitializers(this, _unacknowledgedCount_initializers, 0));
79
+ get unacknowledgedCount() { return this.#unacknowledgedCount_accessor_storage; }
80
+ set unacknowledgedCount(value) { this.#unacknowledgedCount_accessor_storage = value; }
81
+ lastPushedCount = (__runInitializers(this, _unacknowledgedCount_extraInitializers), 0);
65
82
  constructor() {
66
83
  super();
67
84
  const subscription = appstate.logStatePart
@@ -70,27 +87,97 @@ let OpsViewLogs = (() => {
70
87
  this.logState = logState;
71
88
  });
72
89
  this.rxSubscriptions.push(subscription);
90
+ const configEventSubscription = appstate.configEventStatePart
91
+ .select((stateArg) => stateArg)
92
+ .subscribe((eventState) => {
93
+ this.unacknowledgedCount = eventState.unacknowledgedCount;
94
+ });
95
+ this.rxSubscriptions.push(configEventSubscription);
73
96
  }
74
97
  static styles = [
75
98
  cssManager.defaultStyles,
76
99
  shared.viewHostCss,
77
- css ``,
100
+ css `
101
+ .logTabs {
102
+ display: flex;
103
+ gap: 8px;
104
+ margin-bottom: 12px;
105
+ }
106
+
107
+ .logTab {
108
+ border: 1px solid var(--dees-color-border-default);
109
+ background: transparent;
110
+ color: var(--dees-color-text-secondary);
111
+ border-radius: 6px;
112
+ padding: 4px 12px;
113
+ font-size: 12px;
114
+ cursor: pointer;
115
+ transition: all 0.15s ease;
116
+ }
117
+
118
+ .logTab:hover {
119
+ background: var(--dees-color-hover);
120
+ color: var(--dees-color-text-primary);
121
+ }
122
+
123
+ .logTab.active {
124
+ border-color: var(--dees-color-border-strong);
125
+ color: var(--dees-color-text-primary);
126
+ }
127
+
128
+ .logTab .countBadge {
129
+ display: inline-block;
130
+ margin-left: 6px;
131
+ padding: 0 6px;
132
+ border-radius: 8px;
133
+ font-size: 11px;
134
+ font-weight: 600;
135
+ background: ${cssManager.bdTheme('rgba(220, 38, 38, 0.1)', 'rgba(220, 38, 38, 0.25)')};
136
+ color: ${cssManager.bdTheme('#dc2626', '#f87171')};
137
+ }
138
+ `,
78
139
  ];
79
140
  render() {
80
141
  return html `
81
142
  <dees-heading level="3">Logs</dees-heading>
82
143
 
83
- <dees-chart-log
84
- .label=${'Application Logs'}
85
- .autoScroll=${true}
86
- .maxEntries=${2000}
87
- .showMetrics=${true}
88
- ></dees-chart-log>
144
+ <div class="logTabs">
145
+ <button
146
+ class="logTab ${this.activeTab === 'application' ? 'active' : ''}"
147
+ @click=${() => this.switchTab('application')}
148
+ >Application Logs</button>
149
+ <button
150
+ class="logTab ${this.activeTab === 'platform' ? 'active' : ''}"
151
+ @click=${() => this.switchTab('platform')}
152
+ >Platform Events${this.unacknowledgedCount > 0 ? html `<span class="countBadge">${this.unacknowledgedCount}</span>` : ''}</button>
153
+ </div>
154
+
155
+ ${this.activeTab === 'application' ? html `
156
+ <dees-chart-log
157
+ .label=${'Application Logs'}
158
+ .autoScroll=${true}
159
+ .maxEntries=${2000}
160
+ .showMetrics=${true}
161
+ ></dees-chart-log>
162
+ ` : html `
163
+ <ops-platform-events></ops-platform-events>
164
+ `}
89
165
  `;
90
166
  }
167
+ switchTab(tab) {
168
+ if (this.activeTab === tab)
169
+ return;
170
+ this.activeTab = tab;
171
+ if (tab === 'application') {
172
+ // The chart element is recreated on tab switch; re-push everything.
173
+ this.lastPushedCount = 0;
174
+ }
175
+ }
91
176
  async connectedCallback() {
92
177
  super.connectedCallback();
93
178
  this.lastPushedCount = 0;
179
+ // Populate the platform-events badge without requiring a tab visit.
180
+ void appstate.configEventStatePart.dispatchAction(appstate.fetchConfigEventsAction, null);
94
181
  // Only fetch if state is empty (streaming will handle new entries)
95
182
  if (this.logState.recentLogs.length === 0) {
96
183
  await appstate.logStatePart.dispatchAction(appstate.fetchRecentLogsAction, { limit: 100 });
@@ -156,4 +243,4 @@ let OpsViewLogs = (() => {
156
243
  return OpsViewLogs = _classThis;
157
244
  })();
158
245
  export { OpsViewLogs };
159
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctbG9ncy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzX3dlYi9lbGVtZW50cy9vcHMtdmlldy1sb2dzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEtBQUssTUFBTSxNQUFNLG1CQUFtQixDQUFDO0FBQzVDLE9BQU8sS0FBSyxRQUFRLE1BQU0sZ0JBQWdCLENBQUM7QUFFM0MsT0FBTyxFQUNMLFdBQVcsRUFDWCxhQUFhLEVBQ2IsSUFBSSxFQUNKLEtBQUssRUFDTCxHQUFHLEVBQ0gsVUFBVSxHQUNYLE1BQU0sNkJBQTZCLENBQUM7SUFHeEIsV0FBVzs0QkFEdkIsYUFBYSxDQUFDLGVBQWUsQ0FBQzs7OztzQkFDRSxXQUFXOzs7OzJCQUFuQixTQUFRLFdBQVc7Ozs7b0NBQ3pDLEtBQUssRUFBRTtZQUNSLDZLQUFTLFFBQVEsNkJBQVIsUUFBUSwyRkFJZjtZQU5KLDZLQXNHQzs7OztRQXBHQyw2RUFBd0M7WUFDdEMsVUFBVSxFQUFFLEVBQUU7WUFDZCxXQUFXLEVBQUUsS0FBSztZQUNsQixPQUFPLEVBQUUsRUFBRTtTQUNaLEVBQUM7UUFKRixJQUFTLFFBQVEsOENBSWY7UUFKRixJQUFTLFFBQVEsb0RBSWY7UUFFTSxlQUFlLDBEQUFHLENBQUMsRUFBQztRQUU1QjtZQUNFLEtBQUssRUFBRSxDQUFDO1lBQ1IsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLFlBQVk7aUJBQ3ZDLE1BQU0sQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsUUFBUSxDQUFDO2lCQUM5QixTQUFTLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRTtnQkFDdEIsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7WUFDM0IsQ0FBQyxDQUFDLENBQUM7WUFDTCxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUMxQyxDQUFDO1FB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
246
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctbG9ncy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzX3dlYi9lbGVtZW50cy9vcHMtdmlldy1sb2dzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEtBQUssTUFBTSxNQUFNLG1CQUFtQixDQUFDO0FBQzVDLE9BQU8sS0FBSyxRQUFRLE1BQU0sZ0JBQWdCLENBQUM7QUFDM0MsT0FBTywwQkFBMEIsQ0FBQztBQUVsQyxPQUFPLEVBQ0wsV0FBVyxFQUNYLGFBQWEsRUFDYixJQUFJLEVBQ0osS0FBSyxFQUNMLEdBQUcsRUFDSCxVQUFVLEdBQ1gsTUFBTSw2QkFBNkIsQ0FBQztJQUd4QixXQUFXOzRCQUR2QixhQUFhLENBQUMsZUFBZSxDQUFDOzs7O3NCQUNFLFdBQVc7Ozs7Ozs7Ozs7MkJBQW5CLFNBQVEsV0FBVzs7OztvQ0FDekMsS0FBSyxFQUFFO3FDQU9QLEtBQUssRUFBRTsrQ0FHUCxLQUFLLEVBQUU7WUFUUiw2S0FBUyxRQUFRLDZCQUFSLFFBQVEsMkZBSWY7WUFHRixnTEFBUyxTQUFTLDZCQUFULFNBQVMsNkZBQTZDO1lBRy9ELDhNQUFTLG1CQUFtQiw2QkFBbkIsbUJBQW1CLGlIQUFLO1lBWm5DLDZLQWtMQzs7OztRQWhMQyw2RUFBd0M7WUFDdEMsVUFBVSxFQUFFLEVBQUU7WUFDZCxXQUFXLEVBQUUsS0FBSztZQUNsQixPQUFPLEVBQUUsRUFBRTtTQUNaLEVBQUM7UUFKRixJQUFTLFFBQVEsOENBSWY7UUFKRixJQUFTLFFBQVEsb0RBSWY7UUFHRixzSUFBaUQsYUFBYSxHQUFDO1FBQS9ELElBQVMsU0FBUywrQ0FBNkM7UUFBL0QsSUFBUyxTQUFTLHFEQUE2QztRQUcvRCwySkFBK0IsQ0FBQyxHQUFDO1FBQWpDLElBQVMsbUJBQW1CLHlEQUFLO1FBQWpDLElBQVMsbUJBQW1CLCtEQUFLO1FBRXpCLGVBQWUscUVBQUcsQ0FBQyxFQUFDO1FBRTVCO1lBQ0UsS0FBSyxFQUFFLENBQUM7WUFDUixNQUFNLFlBQVksR0FBRyxRQUFRLENBQUMsWUFBWTtpQkFDdkMsTUFBTSxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxRQUFRLENBQUM7aUJBQzlCLFNBQVMsQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFO2dCQUN0QixJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztZQUMzQixDQUFDLENBQUMsQ0FBQztZQUNMLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQ3hDLE1BQU0sdUJBQXVCLEdBQUcsUUFBUSxDQUFDLG9CQUFvQjtpQkFDMUQsTUFBTSxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxRQUFRLENBQUM7aUJBQzlCLFNBQVMsQ0FBQyxDQUFDLFVBQVUsRUFBRSxFQUFFO2dCQUN4QixJQUFJLENBQUMsbUJBQW1CLEdBQUcsVUFBVSxDQUFDLG1CQUFtQixDQUFDO1lBQzVELENBQUMsQ0FBQyxDQUFDO1lBQ0wsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsdUJBQXVCLENBQUMsQ0FBQztRQUNyRCxDQUFDO1FBRU0sTUFBTSxDQUFDLE1BQU0sR0FBRztZQUNyQixVQUFVLENBQUMsYUFBYTtZQUN4QixNQUFNLENBQUMsV0FBVztZQUNsQixHQUFHLENBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O3NCQW1DZSxVQUFVLENBQUMsT0FBTyxDQUFDLHdCQUF3QixFQUFFLHlCQUF5QixDQUFDO2lCQUM1RSxVQUFVLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUM7O0tBRXBEO1NBQ0YsQ0FBQztRQUVLLE1BQU07WUFDWCxPQUFPLElBQUksQ0FBQTs7Ozs7MEJBS1csSUFBSSxDQUFDLFNBQVMsS0FBSyxhQUFhLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRTttQkFDdkQsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUM7OzswQkFHNUIsSUFBSSxDQUFDLFNBQVMsS0FBSyxVQUFVLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRTttQkFDcEQsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUM7MEJBQ3pCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQSw0QkFBNEIsSUFBSSxDQUFDLG1CQUFtQixTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUU7OztRQUd2SCxJQUFJLENBQUMsU0FBUyxLQUFLLGFBQWEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFBOzttQkFFNUIsa0JBQWtCO3dCQUNiLElBQUk7d0JBQ0osSUFBSTt5QkFDSCxJQUFJOztPQUV0QixDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUE7O09BRVA7S0FDRixDQUFDO1FBQ0osQ0FBQztRQUVPLFNBQVMsQ0FBQyxHQUErQjtZQUMvQyxJQUFJLElBQUksQ0FBQyxTQUFTLEtBQUssR0FBRztnQkFBRSxPQUFPO1lBQ25DLElBQUksQ0FBQyxTQUFTLEdBQUcsR0FBRyxDQUFDO1lBQ3JCLElBQUksR0FBRyxLQUFLLGFBQWEsRUFBRSxDQUFDO2dCQUMxQixvRUFBb0U7Z0JBQ3BFLElBQUksQ0FBQyxlQUFlLEdBQUcsQ0FBQyxDQUFDO1lBQzNCLENBQUM7UUFDSCxDQUFDO1FBRUQsS0FBSyxDQUFDLGlCQUFpQjtZQUNyQixLQUFLLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUMxQixJQUFJLENBQUMsZUFBZSxHQUFHLENBQUMsQ0FBQztZQUN6QixvRUFBb0U7WUFDcEUsS0FBSyxRQUFRLENBQUMsb0JBQW9CLENBQUMsY0FBYyxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUMxRixtRUFBbUU7WUFDbkUsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQzFDLE1BQU0sUUFBUSxDQUFDLFlBQVksQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUM7WUFDN0YsQ0FBQztRQUNILENBQUM7UUFFRCxLQUFLLENBQUMsT0FBTyxDQUFDLGlCQUFtQztZQUMvQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLENBQUM7WUFDakMsSUFBSSxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztnQkFDdEMsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQ3pCLENBQUM7UUFDSCxDQUFDO1FBRU8sS0FBSyxDQUFDLGVBQWU7WUFDM0IsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFVBQVUsRUFBRSxhQUFhLENBQUMsZ0JBQWdCLENBQVEsQ0FBQztZQUN6RSxJQUFJLENBQUMsUUFBUTtnQkFBRSxPQUFPO1lBRXRCLCtEQUErRDtZQUMvRCxNQUFNLFFBQVEsQ0FBQyxjQUFjLENBQUM7WUFFOUIsNERBQTREO1lBQzVELElBQUksQ0FBQyxRQUFRLENBQUMsYUFBYSxFQUFFLENBQUM7Z0JBQzVCLE1BQU0sSUFBSSxPQUFPLENBQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRTtvQkFDbEMsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO29CQUNqQixNQUFNLFdBQVcsR0FBRyxHQUFHLENBQUMsQ0FBQywwQkFBMEI7b0JBQ25ELE1BQU0sS0FBSyxHQUFHLEdBQUcsRUFBRTt3QkFDakIsSUFBSSxRQUFRLENBQUMsYUFBYSxFQUFFLENBQUM7NEJBQUMsT0FBTyxFQUFFLENBQUM7NEJBQUMsT0FBTzt3QkFBQyxDQUFDO3dCQUNsRCxJQUFJLEVBQUUsUUFBUSxJQUFJLFdBQVcsRUFBRSxDQUFDOzRCQUM5QixPQUFPLENBQUMsSUFBSSxDQUFDLGlEQUFpRCxDQUFDLENBQUM7NEJBQ2hFLE9BQU8sRUFBRSxDQUFDLENBQUMsdUNBQXVDOzRCQUNsRCxPQUFPO3dCQUNULENBQUM7d0JBQ0QsVUFBVSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztvQkFDeEIsQ0FBQyxDQUFDO29CQUNGLEtBQUssRUFBRSxDQUFDO2dCQUNWLENBQUMsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztZQUVELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1lBQzlDLElBQUksSUFBSSxDQUFDLGVBQWUsS0FBSyxDQUFDLElBQUksVUFBVSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDeEQsaUNBQWlDO2dCQUNqQyxRQUFRLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxDQUFDO2dCQUMvQixJQUFJLENBQUMsZUFBZSxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUM7WUFDM0MsQ0FBQztpQkFBTSxJQUFJLFVBQVUsQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO2dCQUNwRCxxQ0FBcUM7Z0JBQ3JDLE1BQU0sVUFBVSxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDO2dCQUMxRCxRQUFRLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxDQUFDO2dCQUMvQixJQUFJLENBQUMsZUFBZSxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUM7WUFDM0MsQ0FBQztRQUNILENBQUM7UUFFTyxtQkFBbUI7WUFDekIsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0JBQzVDLFNBQVMsRUFBRSxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsV0FBVyxFQUFFO2dCQUNoRCxLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQTRDO2dCQUN2RCxPQUFPLEVBQUUsR0FBRyxDQUFDLE9BQU87Z0JBQ3BCLE1BQU0sRUFBRSxHQUFHLENBQUMsUUFBUTthQUNyQixDQUFDLENBQUMsQ0FBQztRQUNOLENBQUM7O1lBaExVLHVEQUFXOzs7OztTQUFYLFdBQVcifQ==
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@serve.zone/dcrouter",
3
3
  "private": false,
4
- "version": "17.2.4",
4
+ "version": "17.4.0",
5
5
  "description": "A multifaceted routing service handling mail and SMS delivery functions.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -39,7 +39,7 @@
39
39
  "@push.rocks/smartacme": "^9.5.0",
40
40
  "@push.rocks/smartbucket": "^4.6.1",
41
41
  "@push.rocks/smartchallenge": "^1.1.0",
42
- "@push.rocks/smartdata": "^7.3.1",
42
+ "@push.rocks/smartdata": "^7.4.0",
43
43
  "@push.rocks/smartdb": "^2.14.0",
44
44
  "@push.rocks/smartdns": "^7.12.1",
45
45
  "@push.rocks/smartfs": "^1.6.0",
@@ -49,7 +49,7 @@
49
49
  "@push.rocks/smartmcp": "^0.2.0",
50
50
  "@push.rocks/smartmetrics": "^3.0.3",
51
51
  "@push.rocks/smartmigration": "1.4.1",
52
- "@push.rocks/smartmta": "^8.0.1",
52
+ "@push.rocks/smartmta": "^8.1.1",
53
53
  "@push.rocks/smartnetwork": "^4.8.0",
54
54
  "@push.rocks/smartpath": "^6.0.0",
55
55
  "@push.rocks/smartpromise": "^4.2.4",
@@ -63,7 +63,7 @@
63
63
  "@push.rocks/smartvpn": "1.21.0",
64
64
  "@push.rocks/taskbuffer": "^8.0.2",
65
65
  "@serve.zone/catalog": "^2.16.0",
66
- "@serve.zone/interfaces": "^16.3.0",
66
+ "@serve.zone/interfaces": "^17.1.0",
67
67
  "@serve.zone/remoteingress": "^5.0.0",
68
68
  "@tsclass/tsclass": "^9.5.1",
69
69
  "@types/qrcode": "^1.5.6",
package/readme.plan.md ADDED
@@ -0,0 +1,22 @@
1
+ # Platform refactoring program
2
+
3
+ Status: approved 2026-07-21, derived from that day's incident ledger (deploy-verification crashes, unbounded scans, DNS/IP logic duplication, version-skew scares). Upstream foundations are RELEASED; consumer adoption and the dcrouter-internal refactors are sequenced below — do not start them mid-wave, fold them into the next natural release of each surface.
4
+
5
+ ## Shipped upstream (adopt on next dependency bump)
6
+
7
+ 1. **Typed-request error boundary — already existed.** @api.global/typedrequest ≥ 3.3.2 sanitizes every handler/middleware throw into a structured error envelope (no socket destruction). Action: ensure every deployed consumer (cloudly, dcrouter, coreflow, onebox) resolves ≥ 3.3.2 and remove any local try/catch layers that only re-implement it.
8
+ 2. **Canonical IP helpers — @push.rocks/smartnetwork 4.9.0.** `isPublicIpAddress`, `toReverseDnsName` (non-throwing), `expandIpv6`, alongside the existing `getIpVersion`/`isValidIpv4`/`isValidIpv6`. Adoption: dcrouter ts/email/classes.mail-dns-sync.ts drops its private `toReverseDnsName`/`expandIpv6` (and the `Invalid IP address:` throw); cloudly's `isPublicDeploymentAddress` (classes.deploymentoperationmanager.ts) delegates to `isPublicIpAddress` (IPv4 semantics are byte-identical; IPv6 is 2000::/3 minus 2001:db8::/32).
9
+ 3. **Cursor pagination as the org default — @push.rocks/smartdata 7.4.0.** `SmartDataDbDoc.getPagedInstances({ filter, sortField, sortDirection, uniqueField, limit ≤ 1000, cursor, withTotal })` — seek pagination with a unique tiebreaker, never materializes the full set. Adoption: OpsConfigEventDoc.findFiltered becomes a thin wrapper; every future list surface uses it. Review rule: a list API without a cursor is a defect.
10
+ 4. **Binary provenance — @git.zone/tsrust 1.7.0.** Every built binary gets a JSON trailer (project, version, git commit, target, builtAt, tsrust version); `tsrust inspect <binary>` prints it. Adoption: bump tsrust across the Rust-bridge repos (smartdb, smartproxy, smartmta, smartnetwork, coretraffic, remoteingress) on their next releases; deployment verification can then assert binary identity instead of guessing (see the strings-on-x86 pitfall in smartdb readme.hints.md).
11
+
12
+ ## Sequenced (do NOT start mid-wave)
13
+
14
+ 5. **Ops-handler base class (dcrouter).** 25 handler files carry 11 copies of the requireAuth wrapper and 49 hand-written "manager not initialized" guards. Introduce a base with `registerMethod(name, scope, handlerFn)` doing auth, manager guard, error boundary, and realtime invalidation; migrate handlers opportunistically (new handlers must use it; old ones migrate when touched).
15
+ 6. **Composition-root service table (dcrouter).** classes.dcrouter.ts (2,326 lines) has 20 near-identical addService blocks with drift (withRetry presence, error logging). Replace with a declarative registration table; behavior-preserving.
16
+ 7. **Mail-detail contract dedup (interfaces + catalog + dcrouter).** IEmailDetail and friends exist twice by convention ("matches @serve.zone/catalog"). Move the contract into @serve.zone/interfaces; catalog and dcrouter consume it. Sequence AFTER the in-flight mail-detail wave (countdown, verdicts, transfer stats) fully lands — one migration wave, no compatibility layer.
17
+ 8. **Version-skew hardening (conventions).** (a) Tolerant-reader parsing for persisted operation/evidence/state documents that cross release boundaries; (b) version handshake wherever a live process or socket is reused across upgrades (LocalSmartDb engine reuse — smartdb plan Track 6; gateway-client policyGeneration is the precedent).
18
+
19
+ ## Non-goals
20
+
21
+ - No big-bang rewrites: every item lands either additive-upstream or inside a surface's next natural release.
22
+ - No compatibility layers (org rule): consumers move to the canonical helpers outright when their wave touches the code.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '17.2.4',
6
+ version: '17.4.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -25,7 +25,7 @@ import { createMigrationRunner } from '../ts_migrations/index.js';
25
25
  import { commitinfo } from './00_commitinfo_data.js';
26
26
 
27
27
  import { OpsServer } from './opsserver/index.js';
28
- import { MetricsManager } from './monitoring/index.js';
28
+ import { MetricsManager, OpsEventManager } from './monitoring/index.js';
29
29
  import { RadiusServer, type IRadiusServerConfig } from './radius/index.js';
30
30
  import { RemoteIngressHubLifecycle, RemoteIngressManager, TunnelManager, buildEdgeOnlyDerivationRoutes } from './remoteingress/index.js';
31
31
  import { VpnAccessResolver, VpnManager, type IVpnManagerConfig } from './vpn/index.js';
@@ -35,6 +35,7 @@ import { SecurityLogger, ContentScanner, IPReputationChecker, SecurityPolicyMana
35
35
  import { type IHttp3Config, augmentRoutesWithHttp3 } from './http3/index.js';
36
36
  import { DnsManager } from './dns/manager.dns.js';
37
37
  import { DnsServerRuntime } from './dns/classes.dns-server-runtime.js';
38
+ import { GatewayRouteDnsReconciler } from './dns/classes.gateway-route-dns-reconciler.js';
38
39
  import { AcmeConfigManager } from './acme/manager.acme-config.js';
39
40
  import { SmartAcmeLifecycle } from './acme/classes.smartacme-lifecycle.js';
40
41
  import { AcceptedEmailSpool, EmailDomainManager, EmailRouteBuilder, EmailSettingsManager, MailDnsSync, MailEdgeEligibility, MailEgressCoordinator, ReleasedRemoteIngressEgressIdentitySource, SmartMtaBlobStorageManager, SmartMtaStorageManager, WorkAppMailManager, type ISmartMtaBlobStorageConfig, type TSmartMtaQueueItemLike } from './email/index.js';
@@ -311,6 +312,10 @@ export class DcRouter {
311
312
 
312
313
  // Domain / DNS management (DB-backed providers, domains, records)
313
314
  public dnsManager?: DnsManager;
315
+ public gatewayRouteDnsReconciler?: GatewayRouteDnsReconciler;
316
+
317
+ // Durable, acknowledgeable platform configuration events (DB-backed)
318
+ public opsEventManager?: OpsEventManager;
314
319
 
315
320
  // ACME configuration (DB-backed singleton, replaces tls.contactEmail)
316
321
  public acmeConfigManager?: AcmeConfigManager;
@@ -464,6 +469,27 @@ export class DcRouter {
464
469
  .withRetry({ maxRetries: 1, baseDelayMs: 1000 }),
465
470
  );
466
471
 
472
+ // OpsEventManager: optional, depends on DcRouterDb — durable acknowledgeable
473
+ // platform configuration events (config conflicts, not runtime errors).
474
+ if (this.options.dbConfig?.enabled !== false) {
475
+ this.serviceManager.addService(
476
+ new plugins.taskbuffer.Service('OpsEventManager')
477
+ .optional()
478
+ .dependsOn('DcRouterDb')
479
+ .withStart(async () => {
480
+ this.opsEventManager = new OpsEventManager();
481
+ await this.opsEventManager.start();
482
+ })
483
+ .withStop(async () => {
484
+ if (this.opsEventManager) {
485
+ await this.opsEventManager.stop();
486
+ this.opsEventManager = undefined;
487
+ }
488
+ })
489
+ .withRetry({ maxRetries: 1, baseDelayMs: 500 }),
490
+ );
491
+ }
492
+
467
493
  // DnsManager: optional, depends on DcRouterDb — owns DB-backed DNS state
468
494
  // (providers, domains, records). Must run before SmartProxy so ACME DNS-01
469
495
  // wiring can look up providers.
@@ -727,6 +753,27 @@ export class DcRouter {
727
753
  );
728
754
  }
729
755
 
756
+ // Gateway route DNS reconciliation starts only after durable route config
757
+ // and DNS providers are available.
758
+ if (this.options.dbConfig?.enabled !== false) {
759
+ this.serviceManager.addService(
760
+ new plugins.taskbuffer.Service('GatewayRouteDnsReconciler')
761
+ .optional()
762
+ .dependsOn('ConfigManagers', 'DnsManager')
763
+ .withStart(async () => {
764
+ this.gatewayRouteDnsReconciler = new GatewayRouteDnsReconciler(this);
765
+ await this.gatewayRouteDnsReconciler.start();
766
+ })
767
+ .withStop(async () => {
768
+ if (this.gatewayRouteDnsReconciler) {
769
+ await this.gatewayRouteDnsReconciler.stop();
770
+ this.gatewayRouteDnsReconciler = undefined;
771
+ }
772
+ })
773
+ .withRetry({ maxRetries: 1, baseDelayMs: 500 }),
774
+ );
775
+ }
776
+
730
777
  // Email Server: optional, depends on SmartProxy and durable DB persistence.
731
778
  if (this.options.dbConfig?.enabled !== false) {
732
779
  const emailServiceDeps = ['SmartProxy', 'MetricsManager'];
@@ -1672,6 +1719,21 @@ export class DcRouter {
1672
1719
 
1673
1720
 
1674
1721
 
1722
+ /**
1723
+ * Org default: every email domain accepts inbound mail. Domains without an
1724
+ * explicit inbound policy get catch-all store with 30-day retention, so
1725
+ * replies to platform-sent mail are never bounced by omission. Explicit
1726
+ * SmartMTA routes always outrank this no-route fallback.
1727
+ */
1728
+ private applyDefaultInboundPolicy<T extends { inboundPolicy?: { defaultAction: 'store' | 'reject'; retentionDays: number } }>(
1729
+ domainsArg: T[] | undefined,
1730
+ ): T[] {
1731
+ return (domainsArg || []).map((domain) => ({
1732
+ ...domain,
1733
+ inboundPolicy: domain.inboundPolicy ?? { defaultAction: 'store' as const, retentionDays: 30 },
1734
+ }));
1735
+ }
1736
+
1675
1737
  /**
1676
1738
  * Set up unified email handling with pattern-based routing
1677
1739
  * This implements the consolidated emailConfig approach
@@ -1723,6 +1785,7 @@ export class DcRouter {
1723
1785
  let emailConfig: IUnifiedEmailServerOptions = await this.workAppMailManager.applyStoredIdentitiesToEmailConfig({
1724
1786
  ...this.options.emailConfig,
1725
1787
  ports: mappedEmailPorts,
1788
+ domains: this.applyDefaultInboundPolicy(this.options.emailConfig.domains),
1726
1789
  dkimKeyProvisioning: 'caller-managed',
1727
1790
  persistRoutes: this.options.emailConfig.persistRoutes ?? false,
1728
1791
  queue: queueOptions,
@@ -1765,6 +1828,13 @@ export class DcRouter {
1765
1828
  smtpMessage: configuredDecision?.smtpMessage ?? dcrouterDecision.smtpMessage,
1766
1829
  };
1767
1830
  },
1831
+ onAcceptEnvelope: async (context) => {
1832
+ const emailServer = this.emailServer;
1833
+ if (!emailServer) {
1834
+ throw new Error('Email server is not available for durable envelope acceptance');
1835
+ }
1836
+ await this.acceptedEmailSpool.acceptEnvelope(context, emailServer);
1837
+ },
1768
1838
  },
1769
1839
  });
1770
1840
 
@@ -1780,7 +1850,7 @@ export class DcRouter {
1780
1850
  )) {
1781
1851
  emailConfig = await this.workAppMailManager.applyStoredIdentitiesToEmailConfig({
1782
1852
  ...emailConfig,
1783
- domains: [...(this.options.emailConfig?.domains || [])],
1853
+ domains: this.applyDefaultInboundPolicy(this.options.emailConfig?.domains),
1784
1854
  });
1785
1855
  // DkimManager captures its DomainRegistry at construction. Rebuild the
1786
1856
  // unstarted server so per-domain repair failures are absent from