@serve.zone/dcrouter 16.5.0 → 17.1.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.
- package/.smartconfig.json +5 -2
- package/deno.json +1 -1
- package/dist_serve/bundle.js +1316 -1151
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +6 -1
- package/dist_ts/classes.dcrouter.js +119 -41
- package/dist_ts/db/classes.cache.cleaner.d.ts +3 -0
- package/dist_ts/db/classes.cache.cleaner.js +7 -3
- package/dist_ts/db/classes.dcrouter-db.js +1 -2
- package/dist_ts/db/documents/classes.cached.email.d.ts +41 -4
- package/dist_ts/db/documents/classes.cached.email.js +36 -5
- package/dist_ts/db/documents/classes.dns-record.doc.js +3 -1
- package/dist_ts/db/documents/classes.smartmta-storage.doc.d.ts +8 -0
- package/dist_ts/db/documents/classes.smartmta-storage.doc.js +91 -0
- package/dist_ts/db/documents/index.d.ts +1 -0
- package/dist_ts/db/documents/index.js +2 -1
- package/dist_ts/dns/classes.dns-server-runtime.d.ts +0 -1
- package/dist_ts/dns/classes.dns-server-runtime.js +4 -36
- package/dist_ts/dns/index.d.ts +1 -0
- package/dist_ts/dns/index.js +2 -1
- package/dist_ts/dns/manager.dns.d.ts +1 -0
- package/dist_ts/dns/manager.dns.js +49 -14
- package/dist_ts/dns/providers/cloudflare.provider.js +19 -4
- package/dist_ts/dns/txt-record-presentation.d.ts +20 -0
- package/dist_ts/dns/txt-record-presentation.js +131 -0
- package/dist_ts/email/classes.accepted-email-spool.d.ts +32 -3
- package/dist_ts/email/classes.accepted-email-spool.js +174 -13
- package/dist_ts/email/classes.email-domain.manager.d.ts +14 -1
- package/dist_ts/email/classes.email-domain.manager.js +151 -8
- package/dist_ts/email/classes.email-settings.manager.js +2 -1
- package/dist_ts/email/classes.mail-dns-sync.d.ts +31 -5
- package/dist_ts/email/classes.mail-dns-sync.js +508 -117
- package/dist_ts/email/classes.smartmta-blob-storage-manager.d.ts +27 -0
- package/dist_ts/email/classes.smartmta-blob-storage-manager.js +110 -0
- package/dist_ts/email/classes.smartmta-storage-manager.d.ts +6 -8
- package/dist_ts/email/classes.smartmta-storage-manager.js +63 -86
- package/dist_ts/email/classes.workapp-mail-manager.d.ts +1 -0
- package/dist_ts/email/classes.workapp-mail-manager.js +31 -4
- package/dist_ts/email/index.d.ts +4 -0
- package/dist_ts/email/index.js +5 -1
- package/dist_ts/email/mail-dns-txt-policy.d.ts +18 -0
- package/dist_ts/email/mail-dns-txt-policy.js +466 -0
- package/dist_ts/email/reconcile-mail-dns-singleton.d.ts +47 -0
- package/dist_ts/email/reconcile-mail-dns-singleton.js +159 -0
- package/dist_ts/email/smartmta-storage-policy.d.ts +9 -0
- package/dist_ts/email/smartmta-storage-policy.js +58 -0
- package/dist_ts/opsserver/classes.opsserver.d.ts +8 -0
- package/dist_ts/opsserver/classes.opsserver.js +34 -1
- package/dist_ts/opsserver/handlers/certificate.handler.js +25 -4
- package/dist_ts/opsserver/handlers/config.handler.js +9 -5
- package/dist_ts/opsserver/handlers/dns-provider.handler.js +19 -2
- package/dist_ts/opsserver/handlers/dns-record.handler.js +25 -4
- package/dist_ts/opsserver/handlers/domain.handler.js +37 -3
- package/dist_ts/opsserver/handlers/email-domain.handler.js +5 -7
- package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +1 -0
- package/dist_ts/opsserver/handlers/email-ops.handler.js +26 -7
- package/dist_ts/opsserver/handlers/index.d.ts +1 -0
- package/dist_ts/opsserver/handlers/index.js +2 -1
- package/dist_ts/opsserver/handlers/logs.handler.d.ts +1 -1
- package/dist_ts/opsserver/handlers/logs.handler.js +3 -3
- package/dist_ts/opsserver/handlers/realtime.handler.d.ts +37 -0
- package/dist_ts/opsserver/handlers/realtime.handler.js +154 -0
- package/dist_ts/opsserver/handlers/remoteingress.handler.js +22 -1
- package/dist_ts/opsserver/handlers/route-management.handler.js +47 -5
- package/dist_ts/opsserver/handlers/stats.handler.js +3 -5
- package/dist_ts/paths.d.ts +0 -11
- package/dist_ts/paths.js +1 -22
- package/dist_ts/plugins.d.ts +2 -35
- package/dist_ts/plugins.js +3 -63
- package/dist_ts_interfaces/data/email-domain.d.ts +14 -0
- package/dist_ts_interfaces/requests/config.d.ts +1 -1
- package/dist_ts_interfaces/requests/email-ops.d.ts +34 -1
- package/dist_ts_interfaces/requests/index.d.ts +1 -0
- package/dist_ts_interfaces/requests/index.js +2 -1
- package/dist_ts_interfaces/requests/realtime.d.ts +38 -0
- package/dist_ts_interfaces/requests/realtime.js +11 -0
- package/dist_ts_migrations/index.d.ts +8 -0
- package/dist_ts_migrations/index.js +489 -12
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate/certificates.js +9 -2
- package/dist_ts_web/appstate/domains.js +16 -5
- package/dist_ts_web/appstate/email-domains.d.ts +3 -0
- package/dist_ts_web/appstate/email-domains.js +47 -14
- package/dist_ts_web/appstate/email-ops.d.ts +1 -0
- package/dist_ts_web/appstate/email-ops.js +20 -2
- package/dist_ts_web/appstate/realtime.d.ts +27 -0
- package/dist_ts_web/appstate/realtime.js +71 -0
- package/dist_ts_web/appstate/remoteingress.js +9 -3
- package/dist_ts_web/appstate/routes.js +16 -5
- package/dist_ts_web/appstate/runtime.js +135 -7
- package/dist_ts_web/appstate.d.ts +1 -0
- package/dist_ts_web/appstate.js +2 -1
- package/dist_ts_web/elements/email/email-domain-creation-progress.d.ts +33 -0
- package/dist_ts_web/elements/email/email-domain-creation-progress.js +257 -0
- package/dist_ts_web/elements/email/ops-view-email-domains.js +233 -68
- package/dist_ts_web/elements/email/ops-view-emails.d.ts +4 -2
- package/dist_ts_web/elements/email/ops-view-emails.js +25 -13
- package/dist_ts_web/elements/overview/ops-view-config.js +5 -5
- package/package.json +23 -19
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +136 -44
- package/ts/db/classes.cache.cleaner.ts +18 -3
- package/ts/db/classes.dcrouter-db.ts +0 -2
- package/ts/db/documents/classes.cached.email.ts +78 -4
- package/ts/db/documents/classes.dns-record.doc.ts +1 -0
- package/ts/db/documents/classes.smartmta-storage.doc.ts +25 -0
- package/ts/db/documents/index.ts +1 -0
- package/ts/dns/classes.dns-server-runtime.ts +9 -45
- package/ts/dns/index.ts +1 -0
- package/ts/dns/manager.dns.ts +50 -16
- package/ts/dns/providers/cloudflare.provider.ts +21 -3
- package/ts/dns/txt-record-presentation.ts +134 -0
- package/ts/email/classes.accepted-email-spool.ts +220 -14
- package/ts/email/classes.email-domain.manager.ts +201 -7
- package/ts/email/classes.email-settings.manager.ts +1 -0
- package/ts/email/classes.mail-dns-sync.ts +588 -122
- package/ts/email/classes.smartmta-blob-storage-manager.ts +139 -0
- package/ts/email/classes.smartmta-storage-manager.ts +86 -83
- package/ts/email/classes.workapp-mail-manager.ts +47 -4
- package/ts/email/index.ts +4 -0
- package/ts/email/mail-dns-txt-policy.ts +505 -0
- package/ts/email/reconcile-mail-dns-singleton.ts +224 -0
- package/ts/email/smartmta-storage-policy.ts +73 -0
- package/ts/opsserver/classes.opsserver.ts +42 -0
- package/ts/opsserver/handlers/certificate.handler.ts +24 -3
- package/ts/opsserver/handlers/config.handler.ts +9 -5
- package/ts/opsserver/handlers/dns-provider.handler.ts +18 -1
- package/ts/opsserver/handlers/dns-record.handler.ts +24 -3
- package/ts/opsserver/handlers/domain.handler.ts +36 -2
- package/ts/opsserver/handlers/email-domain.handler.ts +4 -5
- package/ts/opsserver/handlers/email-ops.handler.ts +29 -6
- package/ts/opsserver/handlers/index.ts +1 -0
- package/ts/opsserver/handlers/logs.handler.ts +2 -2
- package/ts/opsserver/handlers/realtime.handler.ts +222 -0
- package/ts/opsserver/handlers/remoteingress.handler.ts +21 -0
- package/ts/opsserver/handlers/route-management.handler.ts +46 -4
- package/ts/opsserver/handlers/stats.handler.ts +2 -4
- package/ts/paths.ts +0 -25
- package/ts/plugins.ts +2 -69
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate/certificates.ts +6 -1
- package/ts_web/appstate/domains.ts +11 -4
- package/ts_web/appstate/email-domains.ts +68 -14
- package/ts_web/appstate/email-ops.ts +20 -1
- package/ts_web/appstate/realtime.ts +109 -0
- package/ts_web/appstate/remoteingress.ts +6 -2
- package/ts_web/appstate/routes.ts +11 -4
- package/ts_web/appstate/runtime.ts +176 -7
- package/ts_web/appstate.ts +1 -0
- package/ts_web/elements/email/email-domain-creation-progress.ts +340 -0
- package/ts_web/elements/email/ops-view-email-domains.ts +273 -74
- package/ts_web/elements/email/ops-view-emails.ts +23 -16
- package/ts_web/elements/overview/ops-view-config.ts +4 -4
|
@@ -10,7 +10,13 @@ import {
|
|
|
10
10
|
import * as appstate from '../../appstate.js';
|
|
11
11
|
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
12
12
|
import { viewHostCss } from '../shared/css.js';
|
|
13
|
-
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
13
|
+
import { type IStep, type IStatsTile } from '@design.estate/dees-catalog';
|
|
14
|
+
import {
|
|
15
|
+
findNewEmailDomain,
|
|
16
|
+
getEmailDomainCreationProgress,
|
|
17
|
+
observeNewEmailDomain,
|
|
18
|
+
withAbortableDeadline,
|
|
19
|
+
} from './email-domain-creation-progress.js';
|
|
14
20
|
|
|
15
21
|
declare global {
|
|
16
22
|
interface HTMLElementTagNameMap {
|
|
@@ -416,86 +422,279 @@ export class OpsViewEmailDomains extends DeesElement {
|
|
|
416
422
|
}
|
|
417
423
|
|
|
418
424
|
private async showCreateDialog() {
|
|
419
|
-
const {
|
|
420
|
-
const domainOptions = this.domainsState.domains.map((
|
|
421
|
-
option: `${
|
|
422
|
-
key:
|
|
425
|
+
const { DeesStepper } = await import('@design.estate/dees-catalog');
|
|
426
|
+
const domainOptions = this.domainsState.domains.map((domain) => ({
|
|
427
|
+
option: `${domain.name} (${domain.source})`,
|
|
428
|
+
key: domain.id,
|
|
423
429
|
}));
|
|
430
|
+
const initialProgress = getEmailDomainCreationProgress({
|
|
431
|
+
requestCompleted: false,
|
|
432
|
+
});
|
|
424
433
|
|
|
425
|
-
|
|
426
|
-
|
|
434
|
+
let createArgs: {
|
|
435
|
+
linkedDomainId: string;
|
|
436
|
+
subdomain?: string;
|
|
437
|
+
dkimSelector?: string;
|
|
438
|
+
dkimKeySize?: number;
|
|
439
|
+
rotateKeys?: boolean;
|
|
440
|
+
} | undefined;
|
|
441
|
+
let expectedDomain: string | undefined;
|
|
442
|
+
let progressStepDefinition: IStep;
|
|
443
|
+
|
|
444
|
+
progressStepDefinition = {
|
|
445
|
+
title: 'Create Email Domain',
|
|
446
|
+
allowBack: false,
|
|
427
447
|
content: html`
|
|
428
|
-
<dees-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
.options=${domainOptions}
|
|
434
|
-
.required=${true}
|
|
435
|
-
></dees-input-dropdown>
|
|
436
|
-
<dees-input-text
|
|
437
|
-
.key=${'subdomain'}
|
|
438
|
-
.label=${'Subdomain'}
|
|
439
|
-
.description=${'Leave empty for bare domain, e.g. "mail" for mail.example.com'}
|
|
440
|
-
></dees-input-text>
|
|
441
|
-
<dees-input-text
|
|
442
|
-
.key=${'dkimSelector'}
|
|
443
|
-
.label=${'DKIM Selector'}
|
|
444
|
-
.description=${'Identifier used in DNS record name'}
|
|
445
|
-
.value=${'default'}
|
|
446
|
-
></dees-input-text>
|
|
447
|
-
<dees-input-dropdown
|
|
448
|
-
.key=${'dkimKeySize'}
|
|
449
|
-
.label=${'DKIM Key Size'}
|
|
450
|
-
.options=${[
|
|
451
|
-
{ option: '2048 (recommended)', key: '2048' },
|
|
452
|
-
{ option: '1024', key: '1024' },
|
|
453
|
-
{ option: '4096', key: '4096' },
|
|
454
|
-
]}
|
|
455
|
-
.selectedOption=${{ option: '2048 (recommended)', key: '2048' }}
|
|
456
|
-
></dees-input-dropdown>
|
|
457
|
-
<dees-input-checkbox
|
|
458
|
-
.key=${'rotateKeys'}
|
|
459
|
-
.label=${'Auto-rotate DKIM keys'}
|
|
460
|
-
.value=${false}
|
|
461
|
-
></dees-input-checkbox>
|
|
462
|
-
</dees-form>
|
|
448
|
+
<p style="margin: 0; color: var(--dees-color-text-secondary); line-height: 1.5;">
|
|
449
|
+
dcrouter is creating the domain, preparing its managed DNS plan, and reconciling the
|
|
450
|
+
records with the linked DNS provider. The final state and any scheduled retry remain
|
|
451
|
+
visible here.
|
|
452
|
+
</p>
|
|
463
453
|
`,
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
454
|
+
progressStep: {
|
|
455
|
+
...initialProgress,
|
|
456
|
+
label: 'Email domain setup',
|
|
457
|
+
statusRows: 8,
|
|
458
|
+
autoAdvance: false,
|
|
459
|
+
},
|
|
460
|
+
menuOptions: [],
|
|
461
|
+
validationFunc: async (stepper, _selectedStep, signal) => {
|
|
462
|
+
let observedDomain: interfaces.data.IEmailDomain | undefined;
|
|
463
|
+
let observationError: Error | undefined;
|
|
464
|
+
let observationPromise: Promise<interfaces.data.IEmailDomain | undefined> | undefined;
|
|
465
|
+
const observationController = new AbortController();
|
|
466
|
+
const abortObservation = () => observationController.abort();
|
|
467
|
+
signal?.addEventListener('abort', abortObservation, { once: true });
|
|
468
|
+
|
|
469
|
+
const renderProgress = (
|
|
470
|
+
domainArg: interfaces.data.IEmailDomain | undefined,
|
|
471
|
+
operationArg: interfaces.requests.IEmailDomainActionResult | null,
|
|
472
|
+
requestCompletedArg: boolean,
|
|
473
|
+
) => {
|
|
474
|
+
if (signal?.aborted || !stepper.isConnected) return;
|
|
475
|
+
stepper.updateProgressStep({
|
|
476
|
+
...getEmailDomainCreationProgress({
|
|
477
|
+
domain: domainArg,
|
|
478
|
+
operation: operationArg,
|
|
479
|
+
requestCompleted: requestCompletedArg,
|
|
480
|
+
}),
|
|
481
|
+
statusRows: 8,
|
|
482
|
+
});
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
if (!createArgs || !expectedDomain) {
|
|
487
|
+
throw new Error('Email domain configuration is incomplete');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
stepper.updateProgressStep({
|
|
491
|
+
percentage: 0,
|
|
492
|
+
indeterminate: true,
|
|
493
|
+
showPercentage: false,
|
|
494
|
+
statusText: 'Checking the current email-domain state...',
|
|
495
|
+
terminalLines: ['Checking for an existing email domain'],
|
|
496
|
+
statusRows: 8,
|
|
497
|
+
});
|
|
498
|
+
const initialDomains = await appstate.fetchEmailDomainsSnapshot();
|
|
499
|
+
if (signal?.aborted) return;
|
|
500
|
+
const existingIds = new Set(
|
|
501
|
+
initialDomains
|
|
502
|
+
.filter((domain) => domain.domain.toLowerCase() === expectedDomain)
|
|
503
|
+
.map((domain) => domain.id),
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
renderProgress(undefined, null, false);
|
|
507
|
+
observationPromise = observeNewEmailDomain({
|
|
508
|
+
expectedDomain,
|
|
509
|
+
existingIds,
|
|
510
|
+
signal: observationController.signal,
|
|
511
|
+
listDomains: appstate.fetchEmailDomainsSnapshot,
|
|
512
|
+
getDomain: appstate.fetchEmailDomainSnapshot,
|
|
513
|
+
onDomain: (domain) => {
|
|
514
|
+
observedDomain = domain;
|
|
515
|
+
renderProgress(domain, null, false);
|
|
516
|
+
},
|
|
517
|
+
subscribeToDomains: (listenerArg) => {
|
|
518
|
+
const subscription = appstate.emailDomainsStatePart
|
|
519
|
+
.select((state) => state.domains)
|
|
520
|
+
.subscribe(listenerArg);
|
|
521
|
+
return () => subscription.unsubscribe();
|
|
522
|
+
},
|
|
523
|
+
timeoutMs: 15 * 60_000,
|
|
524
|
+
});
|
|
525
|
+
void observationPromise.then((domain) => {
|
|
526
|
+
if (domain) observedDomain = domain;
|
|
527
|
+
}).catch((error: unknown) => {
|
|
528
|
+
observationError = error instanceof Error
|
|
529
|
+
? error
|
|
530
|
+
: new Error('Failed to observe the new email domain');
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
const createPromise = appstate.emailDomainsStatePart.dispatchAction(
|
|
534
|
+
appstate.createEmailDomainAction,
|
|
535
|
+
createArgs,
|
|
536
|
+
);
|
|
537
|
+
const nextState = await withAbortableDeadline(createPromise, {
|
|
538
|
+
signal,
|
|
539
|
+
timeoutMs: 120_000,
|
|
540
|
+
timeoutMessage:
|
|
541
|
+
'Email domain creation timed out. The server operation may still complete; refresh the domain list before retrying.',
|
|
542
|
+
});
|
|
543
|
+
const operation = nextState.lastOperation;
|
|
544
|
+
const finalDomain =
|
|
545
|
+
operation?.domain ||
|
|
546
|
+
observedDomain ||
|
|
547
|
+
findNewEmailDomain(nextState.domains, expectedDomain, existingIds);
|
|
548
|
+
if (!finalDomain && observationError) {
|
|
549
|
+
throw observationError;
|
|
550
|
+
}
|
|
551
|
+
renderProgress(finalDomain, operation, true);
|
|
552
|
+
if (!signal?.aborted && stepper.isConnected) {
|
|
553
|
+
progressStepDefinition.menuOptions = [
|
|
484
554
|
{
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
dkimSelector: data.dkimSelector || 'default',
|
|
488
|
-
dkimKeySize: keySize,
|
|
489
|
-
rotateKeys: Boolean(data.rotateKeys),
|
|
555
|
+
name: 'Close',
|
|
556
|
+
action: async (stepperArg) => stepperArg?.destroy(),
|
|
490
557
|
},
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
558
|
+
];
|
|
559
|
+
stepper.steps = [...stepper.steps];
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const lifecycleStatus = finalDomain?.reconciliation?.lifecycleStatus;
|
|
563
|
+
if (
|
|
564
|
+
observationPromise &&
|
|
565
|
+
lifecycleStatus !== 'active' &&
|
|
566
|
+
lifecycleStatus !== 'failed'
|
|
567
|
+
) {
|
|
568
|
+
const terminalDomain = await observationPromise;
|
|
569
|
+
if (terminalDomain) {
|
|
570
|
+
observedDomain = terminalDomain;
|
|
571
|
+
renderProgress(terminalDomain, operation, true);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
} catch (error: unknown) {
|
|
575
|
+
const message = error instanceof Error ? error.message : 'Email domain creation failed';
|
|
576
|
+
renderProgress(
|
|
577
|
+
observedDomain,
|
|
578
|
+
{
|
|
579
|
+
success: false,
|
|
580
|
+
message,
|
|
581
|
+
code: 'EMAIL_DOMAIN_CREATE_PROGRESS_FAILED',
|
|
582
|
+
errors: [{
|
|
583
|
+
code: 'EMAIL_DOMAIN_CREATE_PROGRESS_FAILED',
|
|
584
|
+
message,
|
|
585
|
+
retryable: true,
|
|
586
|
+
}],
|
|
587
|
+
},
|
|
588
|
+
true,
|
|
589
|
+
);
|
|
590
|
+
} finally {
|
|
591
|
+
observationController.abort();
|
|
592
|
+
signal?.removeEventListener('abort', abortObservation);
|
|
593
|
+
if (!signal?.aborted && stepper.isConnected) {
|
|
594
|
+
progressStepDefinition.menuOptions = [
|
|
595
|
+
{
|
|
596
|
+
name: 'Close',
|
|
597
|
+
action: async (stepperArg) => stepperArg?.destroy(),
|
|
598
|
+
},
|
|
599
|
+
];
|
|
600
|
+
stepper.steps = [...stepper.steps];
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
await DeesStepper.createAndShow({
|
|
607
|
+
cancelable: false,
|
|
608
|
+
steps: [
|
|
609
|
+
{
|
|
610
|
+
title: 'Add Email Domain',
|
|
611
|
+
content: html`
|
|
612
|
+
<dees-form>
|
|
613
|
+
<dees-input-dropdown
|
|
614
|
+
.key=${'linkedDomainId'}
|
|
615
|
+
.label=${'Domain'}
|
|
616
|
+
.description=${'Select an existing DNS domain'}
|
|
617
|
+
.options=${domainOptions}
|
|
618
|
+
.required=${true}
|
|
619
|
+
></dees-input-dropdown>
|
|
620
|
+
<dees-input-text
|
|
621
|
+
.key=${'subdomain'}
|
|
622
|
+
.label=${'Subdomain'}
|
|
623
|
+
.description=${'Leave empty for bare domain, e.g. "mail" for mail.example.com'}
|
|
624
|
+
></dees-input-text>
|
|
625
|
+
<dees-input-text
|
|
626
|
+
.key=${'dkimSelector'}
|
|
627
|
+
.label=${'DKIM Selector'}
|
|
628
|
+
.description=${'Identifier used in DNS record name'}
|
|
629
|
+
.value=${'default'}
|
|
630
|
+
></dees-input-text>
|
|
631
|
+
<dees-input-dropdown
|
|
632
|
+
.key=${'dkimKeySize'}
|
|
633
|
+
.label=${'DKIM Key Size'}
|
|
634
|
+
.options=${[
|
|
635
|
+
{ option: '2048 (recommended)', key: '2048' },
|
|
636
|
+
{ option: '1024', key: '1024' },
|
|
637
|
+
{ option: '4096', key: '4096' },
|
|
638
|
+
]}
|
|
639
|
+
.selectedOption=${{ option: '2048 (recommended)', key: '2048' }}
|
|
640
|
+
></dees-input-dropdown>
|
|
641
|
+
<dees-input-checkbox
|
|
642
|
+
.key=${'rotateKeys'}
|
|
643
|
+
.label=${'Auto-rotate DKIM keys'}
|
|
644
|
+
.value=${false}
|
|
645
|
+
></dees-input-checkbox>
|
|
646
|
+
</dees-form>
|
|
647
|
+
`,
|
|
648
|
+
menuOptions: [
|
|
649
|
+
{
|
|
650
|
+
name: 'Cancel',
|
|
651
|
+
action: async (stepperArg) => stepperArg?.destroy(),
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
name: 'Create',
|
|
655
|
+
action: async (stepperArg) => {
|
|
656
|
+
if (!stepperArg) return;
|
|
657
|
+
const form = stepperArg.shadowRoot?.querySelector('.selected dees-form') as any;
|
|
658
|
+
if (!form) return;
|
|
659
|
+
const data = await form.collectFormData();
|
|
660
|
+
const linkedDomainId = String(
|
|
661
|
+
typeof data.linkedDomainId === 'object'
|
|
662
|
+
? data.linkedDomainId.key
|
|
663
|
+
: data.linkedDomainId || '',
|
|
664
|
+
);
|
|
665
|
+
const linkedDomain = this.domainsState.domains.find(
|
|
666
|
+
(domain) => domain.id === linkedDomainId,
|
|
667
|
+
);
|
|
668
|
+
if (!linkedDomain) {
|
|
669
|
+
form.setStatus?.('error', 'Select an existing DNS domain.');
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const keySize = Number.parseInt(
|
|
674
|
+
String(
|
|
675
|
+
typeof data.dkimKeySize === 'object'
|
|
676
|
+
? data.dkimKeySize.key
|
|
677
|
+
: data.dkimKeySize || '2048',
|
|
678
|
+
),
|
|
679
|
+
10,
|
|
680
|
+
);
|
|
681
|
+
const subdomain = String(data.subdomain || '').trim().toLowerCase() || undefined;
|
|
682
|
+
expectedDomain = (
|
|
683
|
+
subdomain ? `${subdomain}.${linkedDomain.name}` : linkedDomain.name
|
|
684
|
+
).toLowerCase();
|
|
685
|
+
createArgs = {
|
|
686
|
+
linkedDomainId,
|
|
687
|
+
subdomain,
|
|
688
|
+
dkimSelector: String(data.dkimSelector || 'default').trim() || 'default',
|
|
689
|
+
dkimKeySize: keySize,
|
|
690
|
+
rotateKeys: Boolean(data.rotateKeys),
|
|
691
|
+
};
|
|
692
|
+
stepperArg.goNext();
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
],
|
|
498
696
|
},
|
|
697
|
+
progressStepDefinition,
|
|
499
698
|
],
|
|
500
699
|
});
|
|
501
700
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DeesElement, property, html, customElement, type TemplateResult, css, state, cssManager } from '@design.estate/dees-element';
|
|
2
|
-
import * as plugins from '../../plugins.js';
|
|
3
2
|
import * as appstate from '../../appstate.js';
|
|
4
3
|
import * as shared from '../shared/index.js';
|
|
5
4
|
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
@@ -25,11 +24,18 @@ export class OpsViewEmails extends DeesElement {
|
|
|
25
24
|
accessor isLoading = false;
|
|
26
25
|
|
|
27
26
|
private stateSubscription: any;
|
|
27
|
+
private lastEmailOpsUpdate = 0;
|
|
28
|
+
private detailRequestGeneration = 0;
|
|
28
29
|
|
|
29
30
|
async connectedCallback() {
|
|
30
31
|
await super.connectedCallback();
|
|
31
32
|
this.stateSubscription = appstate.emailOpsStatePart.select().subscribe((state) => {
|
|
32
33
|
this.emails = state.emails;
|
|
34
|
+
const hasNewSnapshot = state.lastUpdated > this.lastEmailOpsUpdate;
|
|
35
|
+
this.lastEmailOpsUpdate = Math.max(this.lastEmailOpsUpdate, state.lastUpdated);
|
|
36
|
+
if (hasNewSnapshot && this.selectedEmail) {
|
|
37
|
+
void this.refreshEmailDetail(this.selectedEmail.id);
|
|
38
|
+
}
|
|
33
39
|
this.isLoading = state.isLoading;
|
|
34
40
|
});
|
|
35
41
|
// Initial fetch
|
|
@@ -80,29 +86,30 @@ export class OpsViewEmails extends DeesElement {
|
|
|
80
86
|
`;
|
|
81
87
|
}
|
|
82
88
|
|
|
83
|
-
private async
|
|
84
|
-
const
|
|
89
|
+
private async refreshEmailDetail(emailIdArg: string, openDetailArg = false): Promise<void> {
|
|
90
|
+
const requestGeneration = ++this.detailRequestGeneration;
|
|
85
91
|
try {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (response.email) {
|
|
97
|
-
this.selectedEmail = response.email;
|
|
98
|
-
this.currentView = 'detail';
|
|
92
|
+
const email = await appstate.fetchEmailDetailSnapshot(emailIdArg);
|
|
93
|
+
if (requestGeneration !== this.detailRequestGeneration) return;
|
|
94
|
+
if (email) {
|
|
95
|
+
this.selectedEmail = email;
|
|
96
|
+
if (openDetailArg) {
|
|
97
|
+
this.currentView = 'detail';
|
|
98
|
+
}
|
|
99
|
+
} else if (this.selectedEmail?.id === emailIdArg) {
|
|
100
|
+
this.handleBack();
|
|
99
101
|
}
|
|
100
102
|
} catch (error) {
|
|
101
103
|
console.error('Failed to fetch email detail:', error);
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
|
|
107
|
+
private async handleEmailClick(e: CustomEvent<interfaces.requests.IEmail>) {
|
|
108
|
+
await this.refreshEmailDetail(e.detail.id, true);
|
|
109
|
+
}
|
|
110
|
+
|
|
105
111
|
private handleBack() {
|
|
112
|
+
this.detailRequestGeneration++;
|
|
106
113
|
this.selectedEmail = null;
|
|
107
114
|
this.currentView = 'list';
|
|
108
115
|
}
|
|
@@ -114,18 +114,18 @@ export class OpsViewConfig extends DeesElement {
|
|
|
114
114
|
|
|
115
115
|
const fields: IConfigField[] = [
|
|
116
116
|
{ key: 'Base Directory', value: sys.baseDir },
|
|
117
|
-
{ key: '
|
|
117
|
+
{ key: 'Legacy Migration Source', value: sys.dataDir },
|
|
118
118
|
{ key: 'Public IP', value: sys.publicIp },
|
|
119
119
|
{ key: 'Proxy IPs', value: proxyIpValues, type: 'pills' },
|
|
120
120
|
{ key: 'Uptime', value: this.formatUptime(sys.uptime) },
|
|
121
|
-
{ key: '
|
|
122
|
-
{ key: '
|
|
121
|
+
{ key: 'Structured Data Backend', value: sys.storageBackend, type: 'badge' },
|
|
122
|
+
{ key: 'Local SmartData Path', value: sys.storagePath },
|
|
123
123
|
];
|
|
124
124
|
|
|
125
125
|
return html`
|
|
126
126
|
<sz-config-section
|
|
127
127
|
title="System"
|
|
128
|
-
subtitle="
|
|
128
|
+
subtitle="Runtime paths and managed persistence"
|
|
129
129
|
icon="lucide:server"
|
|
130
130
|
status="enabled"
|
|
131
131
|
.fields=${fields}
|