hook up SSE for UI android sse (#26656)

For #26207

add server side event setup for the UI when turning on android MDM.

- [ ] Manual QA must be performed in the three main OSs, macOS, Windows
and Linux.
This commit is contained in:
Gabriel Hernandez 2025-02-27 18:45:41 +00:00 committed by GitHub
parent 973fe46c5e
commit 44f2858769
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 106 additions and 8 deletions

View file

@ -1,4 +1,10 @@
import React, { useContext, useState } from "react";
import React, {
useCallback,
useContext,
useEffect,
useRef,
useState,
} from "react";
import { InjectedRouter } from "react-router";
import { useQuery } from "react-query";
@ -21,17 +27,68 @@ import TurnOffAndroidMdmModal from "./components/TurnOffAndroidMdmModal";
const baseClass = "android-mdm-page";
const TurnOnAndroidMdm = () => {
const POPUP_WIDTH = 885;
const POPUP_HEIGHT = 600;
interface ITurnOnAndroidMdmProps {
router: InjectedRouter;
}
const TurnOnAndroidMdm = ({ router }: ITurnOnAndroidMdmProps) => {
const { renderFlash } = useContext(NotificationContext);
// TODO: figure out issue with aborting the SSE fetch when the window is closed
const newWindow = useRef<Window | null>(null);
const [fetchingSignupUrl, setFetchingSignupUrl] = useState(false);
const [setupSse, setSetupSse] = useState(false);
const handleSSE = useCallback(
async (abortController: AbortController) => {
try {
await mdmAndroidAPI.startSSE(abortController.signal);
abortController.abort();
renderFlash("success", "Android MDM turned on successfully.", {
persistOnPageChange: true,
});
setSetupSse(false);
router.push(PATHS.ADMIN_INTEGRATIONS_MDM);
} catch {
renderFlash("error", "Couldn't turn on Android MDM. Please try again.");
setSetupSse(false);
}
},
[renderFlash, router]
);
useEffect(() => {
const abortController = new AbortController();
if (setupSse) {
handleSSE(abortController);
return () => {
abortController.abort();
};
}
}, [setupSse, router, renderFlash, handleSSE]);
const onConnectMdm = async () => {
setFetchingSignupUrl(true);
try {
const res = await mdmAndroidAPI.getSignupUrl();
// TODO: set up SSE for successful android mdm turned on here.
window.open(res.android_enterprise_signup_url, "_blank");
// Calculate the center position
const left = window.screenX + (window.innerWidth - POPUP_WIDTH) / 2;
const top = window.screenY + (window.innerHeight - POPUP_HEIGHT) / 2;
// TODO: figure out issue with aborting the SSE fetch when the window is closed
newWindow.current = window.open(
res.android_enterprise_signup_url,
"_blank",
`width=${POPUP_WIDTH},height=${POPUP_HEIGHT},top=${top},left=${left}`
);
setSetupSse(true);
} catch (e) {
renderFlash("error", "Couldn't connect. Please try again");
}
@ -112,9 +169,6 @@ interface IAndroidMdmPageProps {
const AndroidMdmPage = ({ router }: IAndroidMdmPageProps) => {
const { isAndroidMdmEnabledAndConfigured } = useContext(AppContext);
const { renderFlash } = useContext(NotificationContext);
const [showTurnOffMdmModal, setShowTurnOffMdmModal] = useState(false);
return (
@ -128,7 +182,7 @@ const AndroidMdmPage = ({ router }: IAndroidMdmPageProps) => {
<div className={`${baseClass}__content`}>
{!isAndroidMdmEnabledAndConfigured ? (
<TurnOnAndroidMdm />
<TurnOnAndroidMdm router={router} />
) : (
<TurnOffAndroidMdm
onClickTurnOff={() => setShowTurnOffMdmModal(true)}

View file

@ -1,5 +1,6 @@
import sendRequest from "services";
import endpoints from "utilities/endpoints";
import { authToken } from "utilities/local";
interface IGetAndroidSignupUrlResponse {
android_enterprise_signup_url: string;
@ -24,4 +25,46 @@ export default {
const { MDM_ANDROID_ENTERPRISE } = endpoints;
return sendRequest("DELETE", MDM_ANDROID_ENTERPRISE);
},
/**
* This function starts a Server-Sent Events connection with the fleet server
* to get messages about a successful Android mdm connection. We have to use
* fetch here because the EventSource API does not support setting headers,
* which we need to authenticate the request.
*/
startSSE: (abortSignal: AbortSignal): Promise<void> => {
return new Promise(async (resolve, reject) => {
try {
const response = await fetch(endpoints.MDM_ANDROID_SSE_URL, {
method: "GET",
headers: {
Authorization: `Bearer ${authToken()}`,
},
signal: abortSignal,
});
const reader = response?.body?.getReader();
const decoder = new TextDecoder();
while (true) {
// @ts-ignore
// eslint-disable-next-line no-await-in-loop
const { done, value } = await reader?.read();
if (done) break;
const text = decoder.decode(value);
if (text === "Android Enterprise successfully connected") {
resolve();
break;
}
}
} catch (error) {
if ((error as Error).name === "AbortError") {
// we want to ignore abort errors
console.error("SSE Fetch aborted");
} else {
reject(error);
}
}
});
},
};

View file

@ -94,6 +94,7 @@ export default {
MDM_ANDROID_ENTERPRISE: `/${API_VERSION}/fleet/android_enterprise`,
MDM_ANDROID_SIGNUP_URL: `/${API_VERSION}/fleet/android_enterprise/signup_url`,
MDM_ANDROID_SSE_URL: `/api/${API_VERSION}/fleet/android_enterprise/signup_sse`,
// apple mdm endpoints
MDM_APPLE: `/${API_VERSION}/fleet/mdm/apple`,