feat(email-domains): add email domain management with DNS provisioning, validation, and ops dashboard support

This commit is contained in:
2026-04-12 22:09:20 +00:00
parent 0b81c95de2
commit 433047bbf1
20 changed files with 1366 additions and 6 deletions

View File

@@ -2377,6 +2377,129 @@ export const toggleApiTokenAction = routeManagementStatePart.createAction<{
}
});
// ============================================================================
// Email Domains State
// ============================================================================
export interface IEmailDomainsState {
domains: interfaces.data.IEmailDomain[];
isLoading: boolean;
lastUpdated: number;
}
export const emailDomainsStatePart = await appState.getStatePart<IEmailDomainsState>(
'emailDomains',
{
domains: [],
isLoading: false,
lastUpdated: 0,
},
'soft',
);
export const fetchEmailDomainsAction = emailDomainsStatePart.createAction(
async (statePartArg): Promise<IEmailDomainsState> => {
const context = getActionContext();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetEmailDomains
>('/typedrequest', 'getEmailDomains');
const response = await request.fire({ identity: context.identity });
return {
...currentState,
domains: response.domains,
isLoading: false,
lastUpdated: Date.now(),
};
} catch {
return { ...currentState, isLoading: false };
}
},
);
export const createEmailDomainAction = emailDomainsStatePart.createAction<{
linkedDomainId: string;
dkimSelector?: string;
dkimKeySize?: number;
rotateKeys?: boolean;
rotationIntervalDays?: number;
}>(async (statePartArg, args, actionContext) => {
const context = getActionContext();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_CreateEmailDomain
>('/typedrequest', 'createEmailDomain');
await request.fire({ identity: context.identity!, ...args });
return await actionContext!.dispatch(fetchEmailDomainsAction, null);
} catch {
return currentState;
}
});
export const deleteEmailDomainAction = emailDomainsStatePart.createAction<string>(
async (statePartArg, id, actionContext) => {
const context = getActionContext();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_DeleteEmailDomain
>('/typedrequest', 'deleteEmailDomain');
await request.fire({ identity: context.identity!, id });
return await actionContext!.dispatch(fetchEmailDomainsAction, null);
} catch {
return currentState;
}
},
);
export const validateEmailDomainAction = emailDomainsStatePart.createAction<string>(
async (statePartArg, id, actionContext) => {
const context = getActionContext();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_ValidateEmailDomain
>('/typedrequest', 'validateEmailDomain');
await request.fire({ identity: context.identity!, id });
return await actionContext!.dispatch(fetchEmailDomainsAction, null);
} catch {
return currentState;
}
},
);
export const provisionEmailDomainDnsAction = emailDomainsStatePart.createAction<string>(
async (statePartArg, id, actionContext) => {
const context = getActionContext();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_ProvisionEmailDomainDns
>('/typedrequest', 'provisionEmailDomainDns');
await request.fire({ identity: context.identity!, id });
return await actionContext!.dispatch(fetchEmailDomainsAction, null);
} catch {
return currentState;
}
},
);
// ============================================================================
// Email Domain Standalone Functions
// ============================================================================
export async function fetchEmailDomainDnsRecords(id: string) {
const context = getActionContext();
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetEmailDomainDnsRecords
>('/typedrequest', 'getEmailDomainDnsRecords');
return request.fire({ identity: context.identity!, id });
}
// ============================================================================
// TypedSocket Client for Real-time Log Streaming
// ============================================================================