fix(typescript): tighten TypeScript null safety and error handling across backend and ops UI

This commit is contained in:
2026-03-26 07:40:56 +00:00
parent 0195a21f30
commit 44f2a7f0a9
40 changed files with 414 additions and 451 deletions

View File

@@ -240,7 +240,7 @@ interface IActionContext {
}
const getActionContext = (): IActionContext => {
const identity = loginStatePart.getState().identity;
const identity = loginStatePart.getState()!.identity;
// Treat expired JWTs as no identity — prevents stale persisted sessions from firing requests
if (identity && identity.expiresAt && identity.expiresAt < Date.now()) {
return { identity: null };
@@ -252,7 +252,7 @@ const getActionContext = (): IActionContext => {
export const loginAction = loginStatePart.createAction<{
username: string;
password: string;
}>(async (statePartArg, dataArg) => {
}>(async (statePartArg, dataArg): Promise<ILoginState> => {
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_AdminLoginWithUsernameAndPassword
>('/typedrequest', 'adminLoginWithUsernameAndPassword');
@@ -269,10 +269,10 @@ export const loginAction = loginStatePart.createAction<{
isLoggedIn: true,
};
}
return statePartArg.getState();
} catch (error) {
return statePartArg.getState()!;
} catch (error: unknown) {
console.error('Login failed:', error);
return statePartArg.getState();
return statePartArg.getState()!;
}
});
@@ -300,9 +300,9 @@ export const logoutAction = loginStatePart.createAction(async (statePartArg) =>
});
// Fetch All Stats Action - Using combined endpoint for efficiency
export const fetchAllStatsAction = statsStatePart.createAction(async (statePartArg) => {
export const fetchAllStatsAction = statsStatePart.createAction(async (statePartArg): Promise<IStatsState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
@@ -310,7 +310,7 @@ export const fetchAllStatsAction = statsStatePart.createAction(async (statePartA
const combinedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetCombinedMetrics
>('/typedrequest', 'getCombinedMetrics');
const combinedResponse = await combinedRequest.fire({
identity: context.identity,
sections: {
@@ -332,19 +332,19 @@ export const fetchAllStatsAction = statsStatePart.createAction(async (statePartA
isLoading: false,
error: null,
};
} catch (error) {
} catch (error: unknown) {
return {
...currentState,
isLoading: false,
error: error.message || 'Failed to fetch statistics',
error: (error as Error).message || 'Failed to fetch statistics',
};
}
});
// Fetch Configuration Action (read-only)
export const fetchConfigurationAction = configStatePart.createAction(async (statePartArg) => {
export const fetchConfigurationAction = configStatePart.createAction(async (statePartArg): Promise<IConfigState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
@@ -361,11 +361,11 @@ export const fetchConfigurationAction = configStatePart.createAction(async (stat
isLoading: false,
error: null,
};
} catch (error) {
} catch (error: unknown) {
return {
...currentState,
isLoading: false,
error: error.message || 'Failed to fetch configuration',
error: (error as Error).message || 'Failed to fetch configuration',
};
}
});
@@ -375,9 +375,9 @@ export const fetchRecentLogsAction = logStatePart.createAction<{
limit?: number;
level?: 'debug' | 'info' | 'warn' | 'error';
category?: 'smtp' | 'dns' | 'security' | 'system' | 'email';
}>(async (statePartArg, dataArg) => {
}>(async (statePartArg, dataArg): Promise<ILogState> => {
const context = getActionContext();
if (!context.identity) return statePartArg.getState();
if (!context.identity) return statePartArg.getState()!;
const logsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetRecentLogs
@@ -391,14 +391,14 @@ export const fetchRecentLogsAction = logStatePart.createAction<{
});
return {
...statePartArg.getState(),
...statePartArg.getState()!,
recentLogs: response.logs,
};
});
// Toggle Auto Refresh Action
export const toggleAutoRefreshAction = uiStatePart.createAction(async (statePartArg) => {
const currentState = statePartArg.getState();
export const toggleAutoRefreshAction = uiStatePart.createAction(async (statePartArg): Promise<IUiState> => {
const currentState = statePartArg.getState()!;
return {
...currentState,
autoRefresh: !currentState.autoRefresh,
@@ -406,9 +406,9 @@ export const toggleAutoRefreshAction = uiStatePart.createAction(async (statePart
});
// Set Active View Action
export const setActiveViewAction = uiStatePart.createAction<string>(async (statePartArg, viewName) => {
const currentState = statePartArg.getState();
export const setActiveViewAction = uiStatePart.createAction<string>(async (statePartArg, viewName): Promise<IUiState> => {
const currentState = statePartArg.getState()!;
// If switching to network view, ensure we fetch network data
if (viewName === 'network' && currentState.activeView !== 'network') {
setTimeout(() => {
@@ -451,9 +451,9 @@ export const setActiveViewAction = uiStatePart.createAction<string>(async (state
});
// Fetch Network Stats Action
export const fetchNetworkStatsAction = networkStatePart.createAction(async (statePartArg) => {
export const fetchNetworkStatsAction = networkStatePart.createAction(async (statePartArg): Promise<INetworkState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
@@ -525,9 +525,9 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
// ============================================================================
// Fetch All Emails Action
export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (statePartArg) => {
export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (statePartArg): Promise<IEmailOpsState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
@@ -558,9 +558,9 @@ export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (stateP
// Certificate Actions
// ============================================================================
export const fetchCertificateOverviewAction = certificateStatePart.createAction(async (statePartArg) => {
export const fetchCertificateOverviewAction = certificateStatePart.createAction(async (statePartArg): Promise<ICertificateState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
@@ -589,9 +589,9 @@ export const fetchCertificateOverviewAction = certificateStatePart.createAction(
});
export const reprovisionCertificateAction = certificateStatePart.createAction<string>(
async (statePartArg, domain, actionContext) => {
async (statePartArg, domain, actionContext): Promise<ICertificateState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -599,13 +599,13 @@ export const reprovisionCertificateAction = certificateStatePart.createAction<st
>('/typedrequest', 'reprovisionCertificateDomain');
await request.fire({
identity: context.identity,
identity: context.identity!,
domain,
});
// Re-fetch overview after reprovisioning
return await actionContext.dispatch(fetchCertificateOverviewAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchCertificateOverviewAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to reprovision certificate',
@@ -615,9 +615,9 @@ export const reprovisionCertificateAction = certificateStatePart.createAction<st
);
export const deleteCertificateAction = certificateStatePart.createAction<string>(
async (statePartArg, domain, actionContext) => {
async (statePartArg, domain, actionContext): Promise<ICertificateState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -625,13 +625,13 @@ export const deleteCertificateAction = certificateStatePart.createAction<string>
>('/typedrequest', 'deleteCertificate');
await request.fire({
identity: context.identity,
identity: context.identity!,
domain,
});
// Re-fetch overview after deletion
return await actionContext.dispatch(fetchCertificateOverviewAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchCertificateOverviewAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to delete certificate',
@@ -649,9 +649,9 @@ export const importCertificateAction = certificateStatePart.createAction<{
publicKey: string;
csr: string;
}>(
async (statePartArg, cert, actionContext) => {
async (statePartArg, cert, actionContext): Promise<ICertificateState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -659,13 +659,13 @@ export const importCertificateAction = certificateStatePart.createAction<{
>('/typedrequest', 'importCertificate');
await request.fire({
identity: context.identity,
identity: context.identity!,
cert,
});
// Re-fetch overview after import
return await actionContext.dispatch(fetchCertificateOverviewAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchCertificateOverviewAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to import certificate',
@@ -681,7 +681,7 @@ export async function fetchCertificateExport(domain: string) {
>('/typedrequest', 'exportCertificate');
return request.fire({
identity: context.identity,
identity: context.identity!,
domain,
});
}
@@ -695,16 +695,16 @@ export async function fetchConnectionToken(edgeId: string) {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetRemoteIngressConnectionToken
>('/typedrequest', 'getRemoteIngressConnectionToken');
return request.fire({ identity: context.identity, edgeId });
return request.fire({ identity: context.identity!, edgeId });
}
// ============================================================================
// Remote Ingress Actions
// ============================================================================
export const fetchRemoteIngressAction = remoteIngressStatePart.createAction(async (statePartArg) => {
export const fetchRemoteIngressAction = remoteIngressStatePart.createAction(async (statePartArg): Promise<IRemoteIngressState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
@@ -743,9 +743,9 @@ export const createRemoteIngressAction = remoteIngressStatePart.createAction<{
listenPorts?: number[];
autoDerivePorts?: boolean;
tags?: string[];
}>(async (statePartArg, dataArg, actionContext) => {
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -753,7 +753,7 @@ export const createRemoteIngressAction = remoteIngressStatePart.createAction<{
>('/typedrequest', 'createRemoteIngress');
const response = await request.fire({
identity: context.identity,
identity: context.identity!,
name: dataArg.name,
listenPorts: dataArg.listenPorts,
autoDerivePorts: dataArg.autoDerivePorts,
@@ -762,16 +762,16 @@ export const createRemoteIngressAction = remoteIngressStatePart.createAction<{
if (response.success) {
// Refresh the list
await actionContext.dispatch(fetchRemoteIngressAction, null);
await actionContext!.dispatch(fetchRemoteIngressAction, null);
return {
...statePartArg.getState(),
...statePartArg.getState()!,
newEdgeId: response.edge.id,
};
}
return currentState;
} catch (error) {
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to create edge',
@@ -780,9 +780,9 @@ export const createRemoteIngressAction = remoteIngressStatePart.createAction<{
});
export const deleteRemoteIngressAction = remoteIngressStatePart.createAction<string>(
async (statePartArg, edgeId, actionContext) => {
async (statePartArg, edgeId, actionContext): Promise<IRemoteIngressState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -790,12 +790,12 @@ export const deleteRemoteIngressAction = remoteIngressStatePart.createAction<str
>('/typedrequest', 'deleteRemoteIngress');
await request.fire({
identity: context.identity,
identity: context.identity!,
id: edgeId,
});
return await actionContext.dispatch(fetchRemoteIngressAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchRemoteIngressAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to delete edge',
@@ -810,9 +810,9 @@ export const updateRemoteIngressAction = remoteIngressStatePart.createAction<{
listenPorts?: number[];
autoDerivePorts?: boolean;
tags?: string[];
}>(async (statePartArg, dataArg, actionContext) => {
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -820,7 +820,7 @@ export const updateRemoteIngressAction = remoteIngressStatePart.createAction<{
>('/typedrequest', 'updateRemoteIngress');
await request.fire({
identity: context.identity,
identity: context.identity!,
id: dataArg.id,
name: dataArg.name,
listenPorts: dataArg.listenPorts,
@@ -828,8 +828,8 @@ export const updateRemoteIngressAction = remoteIngressStatePart.createAction<{
tags: dataArg.tags,
});
return await actionContext.dispatch(fetchRemoteIngressAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchRemoteIngressAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to update edge',
@@ -838,9 +838,9 @@ export const updateRemoteIngressAction = remoteIngressStatePart.createAction<{
});
export const regenerateRemoteIngressSecretAction = remoteIngressStatePart.createAction<string>(
async (statePartArg, edgeId) => {
async (statePartArg, edgeId): Promise<IRemoteIngressState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -848,7 +848,7 @@ export const regenerateRemoteIngressSecretAction = remoteIngressStatePart.create
>('/typedrequest', 'regenerateRemoteIngressSecret');
const response = await request.fire({
identity: context.identity,
identity: context.identity!,
id: edgeId,
});
@@ -870,9 +870,9 @@ export const regenerateRemoteIngressSecretAction = remoteIngressStatePart.create
);
export const clearNewEdgeIdAction = remoteIngressStatePart.createAction(
async (statePartArg) => {
async (statePartArg): Promise<IRemoteIngressState> => {
return {
...statePartArg.getState(),
...statePartArg.getState()!,
newEdgeId: null,
};
}
@@ -881,9 +881,9 @@ export const clearNewEdgeIdAction = remoteIngressStatePart.createAction(
export const toggleRemoteIngressAction = remoteIngressStatePart.createAction<{
id: string;
enabled: boolean;
}>(async (statePartArg, dataArg, actionContext) => {
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -891,13 +891,13 @@ export const toggleRemoteIngressAction = remoteIngressStatePart.createAction<{
>('/typedrequest', 'updateRemoteIngress');
await request.fire({
identity: context.identity,
identity: context.identity!,
id: dataArg.id,
enabled: dataArg.enabled,
});
return await actionContext.dispatch(fetchRemoteIngressAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchRemoteIngressAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to toggle edge',
@@ -909,9 +909,9 @@ export const toggleRemoteIngressAction = remoteIngressStatePart.createAction<{
// Route Management Actions
// ============================================================================
export const fetchMergedRoutesAction = routeManagementStatePart.createAction(async (statePartArg) => {
export const fetchMergedRoutesAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
@@ -943,9 +943,9 @@ export const fetchMergedRoutesAction = routeManagementStatePart.createAction(asy
export const createRouteAction = routeManagementStatePart.createAction<{
route: any;
enabled?: boolean;
}>(async (statePartArg, dataArg, actionContext) => {
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -953,13 +953,13 @@ export const createRouteAction = routeManagementStatePart.createAction<{
>('/typedrequest', 'createRoute');
await request.fire({
identity: context.identity,
identity: context.identity!,
route: dataArg.route,
enabled: dataArg.enabled,
});
return await actionContext.dispatch(fetchMergedRoutesAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchMergedRoutesAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to create route',
@@ -968,9 +968,9 @@ export const createRouteAction = routeManagementStatePart.createAction<{
});
export const deleteRouteAction = routeManagementStatePart.createAction<string>(
async (statePartArg, routeId, actionContext) => {
async (statePartArg, routeId, actionContext): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -978,12 +978,12 @@ export const deleteRouteAction = routeManagementStatePart.createAction<string>(
>('/typedrequest', 'deleteRoute');
await request.fire({
identity: context.identity,
identity: context.identity!,
id: routeId,
});
return await actionContext.dispatch(fetchMergedRoutesAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchMergedRoutesAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to delete route',
@@ -995,9 +995,9 @@ export const deleteRouteAction = routeManagementStatePart.createAction<string>(
export const toggleRouteAction = routeManagementStatePart.createAction<{
id: string;
enabled: boolean;
}>(async (statePartArg, dataArg, actionContext) => {
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -1005,13 +1005,13 @@ export const toggleRouteAction = routeManagementStatePart.createAction<{
>('/typedrequest', 'toggleRoute');
await request.fire({
identity: context.identity,
identity: context.identity!,
id: dataArg.id,
enabled: dataArg.enabled,
});
return await actionContext.dispatch(fetchMergedRoutesAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchMergedRoutesAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to toggle route',
@@ -1022,9 +1022,9 @@ export const toggleRouteAction = routeManagementStatePart.createAction<{
export const setRouteOverrideAction = routeManagementStatePart.createAction<{
routeName: string;
enabled: boolean;
}>(async (statePartArg, dataArg, actionContext) => {
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -1032,13 +1032,13 @@ export const setRouteOverrideAction = routeManagementStatePart.createAction<{
>('/typedrequest', 'setRouteOverride');
await request.fire({
identity: context.identity,
identity: context.identity!,
routeName: dataArg.routeName,
enabled: dataArg.enabled,
});
return await actionContext.dispatch(fetchMergedRoutesAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchMergedRoutesAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to set override',
@@ -1047,9 +1047,9 @@ export const setRouteOverrideAction = routeManagementStatePart.createAction<{
});
export const removeRouteOverrideAction = routeManagementStatePart.createAction<string>(
async (statePartArg, routeName, actionContext) => {
async (statePartArg, routeName, actionContext): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -1057,12 +1057,12 @@ export const removeRouteOverrideAction = routeManagementStatePart.createAction<s
>('/typedrequest', 'removeRouteOverride');
await request.fire({
identity: context.identity,
identity: context.identity!,
routeName,
});
return await actionContext.dispatch(fetchMergedRoutesAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchMergedRoutesAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to remove override',
@@ -1075,9 +1075,9 @@ export const removeRouteOverrideAction = routeManagementStatePart.createAction<s
// API Token Actions
// ============================================================================
export const fetchApiTokensAction = routeManagementStatePart.createAction(async (statePartArg) => {
export const fetchApiTokensAction = routeManagementStatePart.createAction(async (statePartArg): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
if (!context.identity) return currentState;
try {
@@ -1108,7 +1108,7 @@ export async function createApiToken(name: string, scopes: interfaces.data.TApiT
>('/typedrequest', 'createApiToken');
return request.fire({
identity: context.identity,
identity: context.identity!,
name,
scopes,
expiresInDays,
@@ -1122,15 +1122,15 @@ export async function rollApiToken(id: string) {
>('/typedrequest', 'rollApiToken');
return request.fire({
identity: context.identity,
identity: context.identity!,
id,
});
}
export const revokeApiTokenAction = routeManagementStatePart.createAction<string>(
async (statePartArg, tokenId, actionContext) => {
async (statePartArg, tokenId, actionContext): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -1138,12 +1138,12 @@ export const revokeApiTokenAction = routeManagementStatePart.createAction<string
>('/typedrequest', 'revokeApiToken');
await request.fire({
identity: context.identity,
identity: context.identity!,
id: tokenId,
});
return await actionContext.dispatch(fetchApiTokensAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchApiTokensAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to revoke token',
@@ -1155,9 +1155,9 @@ export const revokeApiTokenAction = routeManagementStatePart.createAction<string
export const toggleApiTokenAction = routeManagementStatePart.createAction<{
id: string;
enabled: boolean;
}>(async (statePartArg, dataArg, actionContext) => {
}>(async (statePartArg, dataArg, actionContext): Promise<IRouteManagementState> => {
const context = getActionContext();
const currentState = statePartArg.getState();
const currentState = statePartArg.getState()!;
try {
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
@@ -1165,13 +1165,13 @@ export const toggleApiTokenAction = routeManagementStatePart.createAction<{
>('/typedrequest', 'toggleApiToken');
await request.fire({
identity: context.identity,
identity: context.identity!,
id: dataArg.id,
enabled: dataArg.enabled,
});
return await actionContext.dispatch(fetchApiTokensAction, null);
} catch (error) {
return await actionContext!.dispatch(fetchApiTokensAction, null);
} catch (error: unknown) {
return {
...currentState,
error: error instanceof Error ? error.message : 'Failed to toggle token',
@@ -1191,13 +1191,13 @@ socketRouter.addTypedHandler(
new plugins.domtools.plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushLogEntry>(
'pushLogEntry',
async (dataArg) => {
const current = logStatePart.getState();
const current = logStatePart.getState()!;
const updated = [...current.recentLogs, dataArg.entry];
// Cap at 2000 entries
if (updated.length > 2000) {
updated.splice(0, updated.length - 2000);
}
logStatePart.setState({ ...current, recentLogs: updated });
logStatePart.setState({ ...current, recentLogs: updated } as ILogState);
return {};
}
)
@@ -1232,14 +1232,14 @@ async function disconnectSocket() {
async function dispatchCombinedRefreshAction() {
const context = getActionContext();
if (!context.identity) return;
const currentView = uiStatePart.getState().activeView;
const currentView = uiStatePart.getState()!.activeView;
try {
// Always fetch basic stats for dashboard widgets
const combinedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetCombinedMetrics
>('/typedrequest', 'getCombinedMetrics');
const combinedResponse = await combinedRequest.fire({
identity: context.identity,
sections: {
@@ -1252,12 +1252,13 @@ async function dispatchCombinedRefreshAction() {
});
// Update all stats from combined response
const currentStatsState = statsStatePart.getState()!;
statsStatePart.setState({
...statsStatePart.getState(),
serverStats: combinedResponse.metrics.server || statsStatePart.getState().serverStats,
emailStats: combinedResponse.metrics.email || statsStatePart.getState().emailStats,
dnsStats: combinedResponse.metrics.dns || statsStatePart.getState().dnsStats,
securityMetrics: combinedResponse.metrics.security || statsStatePart.getState().securityMetrics,
...currentStatsState,
serverStats: combinedResponse.metrics.server || currentStatsState.serverStats,
emailStats: combinedResponse.metrics.email || currentStatsState.emailStats,
dnsStats: combinedResponse.metrics.dns || currentStatsState.dnsStats,
securityMetrics: combinedResponse.metrics.security || currentStatsState.securityMetrics,
lastUpdated: Date.now(),
isLoading: false,
error: null,
@@ -1284,7 +1285,7 @@ async function dispatchCombinedRefreshAction() {
});
networkStatePart.setState({
...networkStatePart.getState(),
...networkStatePart.getState()!,
connections: connectionsResponse.connections,
connectionsByIP,
throughputRate: {
@@ -1297,14 +1298,15 @@ async function dispatchCombinedRefreshAction() {
throughputHistory: network.throughputHistory || [],
requestsPerSecond: network.requestsPerSecond || 0,
requestsTotal: network.requestsTotal || 0,
backends: network.backends || [],
lastUpdated: Date.now(),
isLoading: false,
error: null,
});
} catch (error) {
} catch (error: unknown) {
console.error('Failed to fetch connections:', error);
networkStatePart.setState({
...networkStatePart.getState(),
...networkStatePart.getState()!,
connections: [],
connectionsByIP,
throughputRate: {
@@ -1317,6 +1319,7 @@ async function dispatchCombinedRefreshAction() {
throughputHistory: network.throughputHistory || [],
requestsPerSecond: network.requestsPerSecond || 0,
requestsTotal: network.requestsTotal || 0,
backends: network.backends || [],
lastUpdated: Date.now(),
isLoading: false,
error: null,
@@ -1359,9 +1362,9 @@ let currentRefreshRate = 1000; // Track current refresh rate to avoid unnecessar
// Initialize auto-refresh when UI state is ready
(() => {
const startAutoRefresh = () => {
const uiState = uiStatePart.getState();
const loginState = loginStatePart.getState();
const uiState = uiStatePart.getState()!;
const loginState = loginStatePart.getState()!;
// Only start if conditions are met and not already running at the same rate
if (uiState.autoRefresh && loginState.isLoggedIn) {
// Check if we need to restart the interval (rate changed or not running)
@@ -1387,9 +1390,9 @@ let currentRefreshRate = 1000; // Track current refresh rate to avoid unnecessar
};
// Watch for relevant changes only
let previousAutoRefresh = uiStatePart.getState().autoRefresh;
let previousRefreshInterval = uiStatePart.getState().refreshInterval;
let previousIsLoggedIn = loginStatePart.getState().isLoggedIn;
let previousAutoRefresh = uiStatePart.getState()!.autoRefresh;
let previousRefreshInterval = uiStatePart.getState()!.refreshInterval;
let previousIsLoggedIn = loginStatePart.getState()!.isLoggedIn;
uiStatePart.state.subscribe((state) => {
// Only restart if relevant values changed
@@ -1420,7 +1423,7 @@ let currentRefreshRate = 1000; // Track current refresh rate to avoid unnecessar
startAutoRefresh();
// Connect TypedSocket if already logged in (e.g., persistent session)
if (loginStatePart.getState().isLoggedIn) {
if (loginStatePart.getState()!.isLoggedIn) {
connectSocket();
}
})();