|
|
|
|
@@ -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();
|
|
|
|
|
}
|
|
|
|
|
})();
|