@serve.zone/dcrouter 13.8.0 → 13.9.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 (37) hide show
  1. package/dist_serve/bundle.js +1622 -1522
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/classes.dcrouter.js +3 -2
  4. package/dist_ts/dns/manager.dns.d.ts +17 -15
  5. package/dist_ts/dns/manager.dns.js +33 -27
  6. package/dist_ts/dns/providers/factory.js +10 -1
  7. package/dist_ts/opsserver/handlers/dns-provider.handler.js +42 -5
  8. package/dist_ts/opsserver/handlers/domain.handler.js +3 -3
  9. package/dist_ts_interfaces/data/dns-provider.d.ts +28 -4
  10. package/dist_ts_interfaces/data/dns-provider.js +15 -1
  11. package/dist_ts_interfaces/data/dns-record.d.ts +9 -7
  12. package/dist_ts_interfaces/data/domain.d.ts +8 -7
  13. package/dist_ts_interfaces/requests/dns-records.d.ts +1 -1
  14. package/dist_ts_interfaces/requests/domains.d.ts +3 -3
  15. package/dist_ts_migrations/index.js +17 -1
  16. package/dist_ts_web/00_commitinfo_data.js +1 -1
  17. package/dist_ts_web/appstate.d.ts +1 -1
  18. package/dist_ts_web/appstate.js +2 -2
  19. package/dist_ts_web/elements/domains/dns-provider-form.d.ts +4 -2
  20. package/dist_ts_web/elements/domains/dns-provider-form.js +11 -5
  21. package/dist_ts_web/elements/domains/ops-view-dns.js +3 -3
  22. package/dist_ts_web/elements/domains/ops-view-domains.d.ts +1 -1
  23. package/dist_ts_web/elements/domains/ops-view-domains.js +10 -10
  24. package/dist_ts_web/elements/domains/ops-view-providers.js +19 -5
  25. package/package.json +3 -3
  26. package/ts/00_commitinfo_data.ts +1 -1
  27. package/ts/classes.dcrouter.ts +2 -1
  28. package/ts/dns/manager.dns.ts +38 -27
  29. package/ts/dns/providers/factory.ts +11 -0
  30. package/ts/opsserver/handlers/dns-provider.handler.ts +41 -3
  31. package/ts/opsserver/handlers/domain.handler.ts +2 -2
  32. package/ts_web/00_commitinfo_data.ts +1 -1
  33. package/ts_web/appstate.ts +1 -1
  34. package/ts_web/elements/domains/dns-provider-form.ts +12 -4
  35. package/ts_web/elements/domains/ops-view-dns.ts +2 -2
  36. package/ts_web/elements/domains/ops-view-domains.ts +9 -9
  37. package/ts_web/elements/domains/ops-view-providers.ts +18 -4
@@ -46,15 +46,28 @@ export class DnsProviderHandler {
46
46
  }
47
47
 
48
48
  private registerHandlers(): void {
49
- // Get all providers
49
+ // Get all providers — prepends the built-in DcRouter pseudo-provider
50
+ // so operators see a uniform "who serves this?" list that includes the
51
+ // authoritative dcrouter alongside external accounts.
50
52
  this.typedrouter.addTypedHandler(
51
53
  new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDnsProviders>(
52
54
  'getDnsProviders',
53
55
  async (dataArg) => {
54
56
  await this.requireAuth(dataArg, 'dns-providers:read');
55
57
  const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
56
- if (!dnsManager) return { providers: [] };
57
- return { providers: await dnsManager.listProviders() };
58
+ const synthetic: interfaces.data.IDnsProviderPublic = {
59
+ id: interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID,
60
+ name: 'DcRouter',
61
+ type: 'dcrouter',
62
+ status: 'ok',
63
+ createdAt: 0,
64
+ updatedAt: 0,
65
+ createdBy: 'system',
66
+ hasCredentials: false,
67
+ builtIn: true,
68
+ };
69
+ const real = dnsManager ? await dnsManager.listProviders() : [];
70
+ return { providers: [synthetic, ...real] };
58
71
  },
59
72
  ),
60
73
  );
@@ -78,6 +91,12 @@ export class DnsProviderHandler {
78
91
  'createDnsProvider',
79
92
  async (dataArg) => {
80
93
  const userId = await this.requireAuth(dataArg, 'dns-providers:write');
94
+ if (dataArg.type === 'dcrouter') {
95
+ return {
96
+ success: false,
97
+ message: 'cannot create built-in provider',
98
+ };
99
+ }
81
100
  const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
82
101
  if (!dnsManager) {
83
102
  return { success: false, message: 'DnsManager not initialized (DB disabled?)' };
@@ -99,6 +118,9 @@ export class DnsProviderHandler {
99
118
  'updateDnsProvider',
100
119
  async (dataArg) => {
101
120
  await this.requireAuth(dataArg, 'dns-providers:write');
121
+ if (dataArg.id === interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID) {
122
+ return { success: false, message: 'cannot edit built-in provider' };
123
+ }
102
124
  const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
103
125
  if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
104
126
  const ok = await dnsManager.updateProvider(dataArg.id, {
@@ -116,6 +138,9 @@ export class DnsProviderHandler {
116
138
  'deleteDnsProvider',
117
139
  async (dataArg) => {
118
140
  await this.requireAuth(dataArg, 'dns-providers:write');
141
+ if (dataArg.id === interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID) {
142
+ return { success: false, message: 'cannot delete built-in provider' };
143
+ }
119
144
  const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
120
145
  if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
121
146
  return await dnsManager.deleteProvider(dataArg.id, dataArg.force ?? false);
@@ -129,6 +154,13 @@ export class DnsProviderHandler {
129
154
  'testDnsProvider',
130
155
  async (dataArg) => {
131
156
  await this.requireAuth(dataArg, 'dns-providers:read');
157
+ if (dataArg.id === interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID) {
158
+ return {
159
+ ok: false,
160
+ error: 'built-in provider has no external connection to test',
161
+ testedAt: Date.now(),
162
+ };
163
+ }
132
164
  const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
133
165
  if (!dnsManager) {
134
166
  return { ok: false, error: 'DnsManager not initialized', testedAt: Date.now() };
@@ -144,6 +176,12 @@ export class DnsProviderHandler {
144
176
  'listProviderDomains',
145
177
  async (dataArg) => {
146
178
  await this.requireAuth(dataArg, 'dns-providers:read');
179
+ if (dataArg.providerId === interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID) {
180
+ return {
181
+ success: false,
182
+ message: 'built-in provider has no external domain listing — use "Add DcRouter Domain" instead',
183
+ };
184
+ }
147
185
  const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
148
186
  if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
149
187
  try {
@@ -71,7 +71,7 @@ export class DomainHandler {
71
71
  ),
72
72
  );
73
73
 
74
- // Create manual domain
74
+ // Create dcrouter-hosted domain
75
75
  this.typedrouter.addTypedHandler(
76
76
  new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateDomain>(
77
77
  'createDomain',
@@ -80,7 +80,7 @@ export class DomainHandler {
80
80
  const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
81
81
  if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
82
82
  try {
83
- const id = await dnsManager.createManualDomain({
83
+ const id = await dnsManager.createDcrouterDomain({
84
84
  name: dataArg.name,
85
85
  description: dataArg.description,
86
86
  createdBy: userId,
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '13.8.0',
6
+ version: '13.9.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -1793,7 +1793,7 @@ export async function fetchProviderDomains(
1793
1793
  return await request.fire({ identity: context.identity, providerId });
1794
1794
  }
1795
1795
 
1796
- export const createManualDomainAction = domainsStatePart.createAction<{
1796
+ export const createDcrouterDomainAction = domainsStatePart.createAction<{
1797
1797
  name: string;
1798
1798
  description?: string;
1799
1799
  }>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
@@ -44,12 +44,15 @@ export class DnsProviderForm extends DeesElement {
44
44
  accessor providerName: string = '';
45
45
 
46
46
  /**
47
- * Currently selected provider type. Initialized to the first descriptor;
48
- * caller can override before mounting (e.g. for edit dialogs).
47
+ * Currently selected provider type. Initialized to the first user-creatable
48
+ * descriptor; caller can override before mounting (e.g. for edit dialogs).
49
+ * The built-in 'dcrouter' pseudo-provider is excluded from the picker —
50
+ * operators cannot create another one.
49
51
  */
50
52
  @state()
51
53
  accessor selectedType: interfaces.data.TDnsProviderType =
52
- interfaces.data.dnsProviderTypeDescriptors[0]?.type ?? 'cloudflare';
54
+ interfaces.data.dnsProviderTypeDescriptors.find((d) => d.type !== 'dcrouter')?.type ??
55
+ 'cloudflare';
53
56
 
54
57
  /** When true, hide the type picker — used in edit dialogs. */
55
58
  @property({ type: Boolean })
@@ -102,7 +105,12 @@ export class DnsProviderForm extends DeesElement {
102
105
  ];
103
106
 
104
107
  public render(): TemplateResult {
105
- const descriptors = interfaces.data.dnsProviderTypeDescriptors;
108
+ // Exclude the built-in 'dcrouter' pseudo-provider from the type picker —
109
+ // operators cannot create another one, it's surfaced at read time by the
110
+ // backend handler instead.
111
+ const descriptors = interfaces.data.dnsProviderTypeDescriptors.filter(
112
+ (d) => d.type !== 'dcrouter',
113
+ );
106
114
  const descriptor = interfaces.data.getDnsProviderTypeDescriptor(this.selectedType);
107
115
 
108
116
  return html`
@@ -80,7 +80,7 @@ export class OpsViewDns extends DeesElement {
80
80
  font-weight: 500;
81
81
  }
82
82
 
83
- .sourceBadge.manual {
83
+ .sourceBadge.local {
84
84
  background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
85
85
  color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
86
86
  }
@@ -184,7 +184,7 @@ export class OpsViewDns extends DeesElement {
184
184
  private domainHint(domainId: string): string {
185
185
  const domain = this.domainsState.domains.find((d) => d.id === domainId);
186
186
  if (!domain) return '';
187
- if (domain.source === 'manual') {
187
+ if (domain.source === 'dcrouter') {
188
188
  return 'Records are served by dcrouter (authoritative).';
189
189
  }
190
190
  return 'Records are stored at the provider — changes here are pushed via the provider API.';
@@ -55,7 +55,7 @@ export class OpsViewDomains extends DeesElement {
55
55
  font-weight: 500;
56
56
  }
57
57
 
58
- .sourceBadge.manual {
58
+ .sourceBadge.dcrouter {
59
59
  background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
60
60
  color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
61
61
  }
@@ -76,7 +76,7 @@ export class OpsViewDomains extends DeesElement {
76
76
  <div class="domainsContainer">
77
77
  <dees-table
78
78
  .heading1=${'Domains'}
79
- .heading2=${'Domains under management — manual (authoritative) or imported from a provider'}
79
+ .heading2=${'Domains under management — served by dcrouter (authoritative) or imported from a provider'}
80
80
  .data=${domains}
81
81
  .showColumnFilters=${true}
82
82
  .displayFunction=${(d: interfaces.data.IDomain) => ({
@@ -90,11 +90,11 @@ export class OpsViewDomains extends DeesElement {
90
90
  })}
91
91
  .dataActions=${[
92
92
  {
93
- name: 'Add Manual Domain',
93
+ name: 'Add DcRouter Domain',
94
94
  iconName: 'lucide:plus',
95
95
  type: ['header' as const],
96
96
  actionFunc: async () => {
97
- await this.showCreateManualDialog();
97
+ await this.showCreateDcrouterDialog();
98
98
  },
99
99
  },
100
100
  {
@@ -168,17 +168,17 @@ export class OpsViewDomains extends DeesElement {
168
168
  d: interfaces.data.IDomain,
169
169
  providersById: Map<string, interfaces.data.IDnsProviderPublic>,
170
170
  ): TemplateResult {
171
- if (d.source === 'manual') {
172
- return html`<span class="sourceBadge manual">Manual</span>`;
171
+ if (d.source === 'dcrouter') {
172
+ return html`<span class="sourceBadge dcrouter">DcRouter</span>`;
173
173
  }
174
174
  const provider = d.providerId ? providersById.get(d.providerId) : undefined;
175
175
  return html`<span class="sourceBadge provider">${provider?.name || 'Provider'}</span>`;
176
176
  }
177
177
 
178
- private async showCreateManualDialog() {
178
+ private async showCreateDcrouterDialog() {
179
179
  const { DeesModal } = await import('@design.estate/dees-catalog');
180
180
  DeesModal.createAndShow({
181
- heading: 'Add Manual Domain',
181
+ heading: 'Add DcRouter Domain',
182
182
  content: html`
183
183
  <dees-form>
184
184
  <dees-input-text .key=${'name'} .label=${'FQDN (e.g. example.com)'} .required=${true}></dees-input-text>
@@ -199,7 +199,7 @@ export class OpsViewDomains extends DeesElement {
199
199
  ?.querySelector('dees-form');
200
200
  if (!form) return;
201
201
  const data = await form.collectFormData();
202
- await appstate.domainsStatePart.dispatchAction(appstate.createManualDomainAction, {
202
+ await appstate.domainsStatePart.dispatchAction(appstate.createDcrouterDomainAction, {
203
203
  name: String(data.name),
204
204
  description: data.description ? String(data.description) : undefined,
205
205
  });
@@ -71,6 +71,11 @@ export class OpsViewProviders extends DeesElement {
71
71
  background: ${cssManager.bdTheme('#f3f4f6', '#1f2937')};
72
72
  color: ${cssManager.bdTheme('#4b5563', '#9ca3af')};
73
73
  }
74
+
75
+ .statusBadge.builtin {
76
+ background: ${cssManager.bdTheme('#e0e7ff', '#1e1b4b')};
77
+ color: ${cssManager.bdTheme('#3730a3', '#a5b4fc')};
78
+ }
74
79
  `,
75
80
  ];
76
81
 
@@ -82,15 +87,21 @@ export class OpsViewProviders extends DeesElement {
82
87
  <div class="providersContainer">
83
88
  <dees-table
84
89
  .heading1=${'Providers'}
85
- .heading2=${'External DNS provider accounts'}
90
+ .heading2=${'Built-in dcrouter + external DNS provider accounts'}
86
91
  .data=${providers}
87
92
  .showColumnFilters=${true}
88
93
  .displayFunction=${(p: interfaces.data.IDnsProviderPublic) => ({
89
94
  Name: p.name,
90
95
  Type: this.providerTypeLabel(p.type),
91
- Status: this.renderStatusBadge(p.status),
92
- 'Last Tested': p.lastTestedAt ? new Date(p.lastTestedAt).toLocaleString() : 'never',
93
- Error: p.lastError || '-',
96
+ Status: p.builtIn
97
+ ? html`<span class="statusBadge builtin">built-in</span>`
98
+ : this.renderStatusBadge(p.status),
99
+ 'Last Tested': p.builtIn
100
+ ? '—'
101
+ : p.lastTestedAt
102
+ ? new Date(p.lastTestedAt).toLocaleString()
103
+ : 'never',
104
+ Error: p.builtIn ? '—' : p.lastError || '-',
94
105
  })}
95
106
  .dataActions=${[
96
107
  {
@@ -116,6 +127,7 @@ export class OpsViewProviders extends DeesElement {
116
127
  name: 'Test Connection',
117
128
  iconName: 'lucide:plug',
118
129
  type: ['inRow', 'contextmenu'] as any,
130
+ actionRelevancyCheckFunc: (p: interfaces.data.IDnsProviderPublic) => !p.builtIn,
119
131
  actionFunc: async (actionData: any) => {
120
132
  const provider = actionData.item as interfaces.data.IDnsProviderPublic;
121
133
  await this.testProvider(provider);
@@ -125,6 +137,7 @@ export class OpsViewProviders extends DeesElement {
125
137
  name: 'Edit',
126
138
  iconName: 'lucide:pencil',
127
139
  type: ['inRow', 'contextmenu'] as any,
140
+ actionRelevancyCheckFunc: (p: interfaces.data.IDnsProviderPublic) => !p.builtIn,
128
141
  actionFunc: async (actionData: any) => {
129
142
  const provider = actionData.item as interfaces.data.IDnsProviderPublic;
130
143
  await this.showEditDialog(provider);
@@ -134,6 +147,7 @@ export class OpsViewProviders extends DeesElement {
134
147
  name: 'Delete',
135
148
  iconName: 'lucide:trash2',
136
149
  type: ['inRow', 'contextmenu'] as any,
150
+ actionRelevancyCheckFunc: (p: interfaces.data.IDnsProviderPublic) => !p.builtIn,
137
151
  actionFunc: async (actionData: any) => {
138
152
  const provider = actionData.item as interfaces.data.IDnsProviderPublic;
139
153
  await this.deleteProvider(provider);