BREAKING CHANGE(email-ops): migrate email operations to catalog-compatible email model and simplify UI/router

This commit is contained in:
2026-02-22 00:45:01 +00:00
parent 4759c4f011
commit 48d3d1218f
12 changed files with 338 additions and 1561 deletions

View File

@@ -67,14 +67,7 @@ export interface ICertificateState {
}
export interface IEmailOpsState {
currentView: 'queued' | 'sent' | 'failed' | 'received' | 'security';
queuedEmails: interfaces.requests.IEmailQueueItem[];
sentEmails: interfaces.requests.IEmailQueueItem[];
failedEmails: interfaces.requests.IEmailQueueItem[];
securityIncidents: interfaces.requests.ISecurityIncident[];
bounceRecords: interfaces.requests.IBounceRecord[];
suppressionList: string[];
selectedEmailId: string | null;
emails: interfaces.requests.IEmail[];
isLoading: boolean;
error: string | null;
lastUpdated: number;
@@ -165,14 +158,7 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
export const emailOpsStatePart = await appState.getStatePart<IEmailOpsState>(
'emailOps',
{
currentView: 'queued',
queuedEmails: [],
sentEmails: [],
failedEmails: [],
securityIncidents: [],
bounceRecords: [],
suppressionList: [],
selectedEmailId: null,
emails: [],
isLoading: false,
error: null,
lastUpdated: 0,
@@ -492,35 +478,22 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
// Email Operations Actions
// ============================================================================
// Set Email Ops View Action
export const setEmailOpsViewAction = emailOpsStatePart.createAction<IEmailOpsState['currentView']>(
async (statePartArg, view) => {
return {
...statePartArg.getState(),
currentView: view,
};
}
);
// Fetch Queued Emails Action
export const fetchQueuedEmailsAction = emailOpsStatePart.createAction(async (statePartArg) => {
// Fetch All Emails Action
export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (statePartArg) => {
const context = getActionContext();
const currentState = statePartArg.getState();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetQueuedEmails
>('/typedrequest', 'getQueuedEmails');
interfaces.requests.IReq_GetAllEmails
>('/typedrequest', 'getAllEmails');
const response = await request.fire({
identity: context.identity,
status: 'pending',
limit: 100,
});
return {
...currentState,
queuedEmails: response.items,
emails: response.emails,
isLoading: false,
error: null,
lastUpdated: Date.now(),
@@ -529,197 +502,11 @@ export const fetchQueuedEmailsAction = emailOpsStatePart.createAction(async (sta
return {
...currentState,
isLoading: false,
error: error instanceof Error ? error.message : 'Failed to fetch queued emails',
error: error instanceof Error ? error.message : 'Failed to fetch emails',
};
}
});
// Fetch Sent Emails Action
export const fetchSentEmailsAction = emailOpsStatePart.createAction(async (statePartArg) => {
const context = getActionContext();
const currentState = statePartArg.getState();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetSentEmails
>('/typedrequest', 'getSentEmails');
const response = await request.fire({
identity: context.identity,
limit: 100,
});
return {
...currentState,
sentEmails: response.items,
isLoading: false,
error: null,
lastUpdated: Date.now(),
};
} catch (error) {
return {
...currentState,
isLoading: false,
error: error instanceof Error ? error.message : 'Failed to fetch sent emails',
};
}
});
// Fetch Failed Emails Action
export const fetchFailedEmailsAction = emailOpsStatePart.createAction(async (statePartArg) => {
const context = getActionContext();
const currentState = statePartArg.getState();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetFailedEmails
>('/typedrequest', 'getFailedEmails');
const response = await request.fire({
identity: context.identity,
limit: 100,
});
return {
...currentState,
failedEmails: response.items,
isLoading: false,
error: null,
lastUpdated: Date.now(),
};
} catch (error) {
return {
...currentState,
isLoading: false,
error: error instanceof Error ? error.message : 'Failed to fetch failed emails',
};
}
});
// Fetch Security Incidents Action
export const fetchSecurityIncidentsAction = emailOpsStatePart.createAction(async (statePartArg) => {
const context = getActionContext();
const currentState = statePartArg.getState();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetSecurityIncidents
>('/typedrequest', 'getSecurityIncidents');
const response = await request.fire({
identity: context.identity,
limit: 100,
});
return {
...currentState,
securityIncidents: response.incidents,
isLoading: false,
error: null,
lastUpdated: Date.now(),
};
} catch (error) {
return {
...currentState,
isLoading: false,
error: error instanceof Error ? error.message : 'Failed to fetch security incidents',
};
}
});
// Fetch Bounce Records Action
export const fetchBounceRecordsAction = emailOpsStatePart.createAction(async (statePartArg) => {
const context = getActionContext();
const currentState = statePartArg.getState();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetBounceRecords
>('/typedrequest', 'getBounceRecords');
const response = await request.fire({
identity: context.identity,
limit: 100,
});
return {
...currentState,
bounceRecords: response.records,
suppressionList: response.suppressionList,
isLoading: false,
error: null,
lastUpdated: Date.now(),
};
} catch (error) {
return {
...currentState,
isLoading: false,
error: error instanceof Error ? error.message : 'Failed to fetch bounce records',
};
}
});
// Resend Failed Email Action
export const resendEmailAction = emailOpsStatePart.createAction<string>(async (statePartArg, emailId) => {
const context = getActionContext();
const currentState = statePartArg.getState();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_ResendEmail
>('/typedrequest', 'resendEmail');
const response = await request.fire({
identity: context.identity,
emailId,
});
if (response.success) {
// Refresh failed emails list
await emailOpsStatePart.dispatchAction(fetchFailedEmailsAction, null);
await emailOpsStatePart.dispatchAction(fetchQueuedEmailsAction, null);
}
return statePartArg.getState();
} catch (error) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to resend email',
};
}
});
// Remove from Suppression List Action
export const removeFromSuppressionListAction = emailOpsStatePart.createAction<string>(
async (statePartArg, email) => {
const context = getActionContext();
const currentState = statePartArg.getState();
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_RemoveFromSuppressionList
>('/typedrequest', 'removeFromSuppressionList');
const response = await request.fire({
identity: context.identity,
email,
});
if (response.success) {
// Refresh bounce records
await emailOpsStatePart.dispatchAction(fetchBounceRecordsAction, null);
}
return statePartArg.getState();
} catch (error) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to remove from suppression list',
};
}
}
);
// ============================================================================
// Certificate Actions
// ============================================================================