diff --git a/ui/public/index.html b/ui/public/index.html index 90c3dbc..5f4f5e8 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -50,9 +50,9 @@ href="https://fonts.googleapis.com/icon?family=Material+Icons" /> Asynq - Monitoring diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 730b6d7..d317753 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -26,7 +26,7 @@ import TimelineIcon from "@material-ui/icons/Timeline"; import DoubleArrowIcon from "@material-ui/icons/DoubleArrow"; import CloseIcon from "@material-ui/icons/Close"; import { AppState } from "./store"; -import { paths } from "./paths"; +import { paths as getPaths } from "./paths"; import { isDarkTheme, useTheme } from "./theme"; import { closeSnackbar } from "./actions/snackbarActions"; import { toggleDrawer } from "./actions/settingsActions"; @@ -156,6 +156,7 @@ function SlideUpTransition(props: TransitionProps) { function App(props: ConnectedProps) { const theme = useTheme(props.themePreference); const classes = useStyles(theme)(); + const paths = getPaths(); return ( diff --git a/ui/src/api.ts b/ui/src/api.ts index c3cf6d0..50f789a 100644 --- a/ui/src/api.ts +++ b/ui/src/api.ts @@ -4,7 +4,7 @@ import queryString from "query-string"; // In production build, API server is on listening on the same port as // the static file server. // In developement, we assume that the API server is listening on port 8080. -const BASE_URL = +const getBaseUrl = () => process.env.NODE_ENV === "production" ? `${window.ROOT_PATH}/api` : `http://localhost:8080${window.ROOT_PATH}/api`; @@ -340,7 +340,7 @@ export interface PaginationOptions extends Record { export async function listQueues(): Promise { const resp = await axios({ method: "get", - url: `${BASE_URL}/queues`, + url: `${getBaseUrl()}/queues`, }); return resp.data; } @@ -348,28 +348,28 @@ export async function listQueues(): Promise { export async function deleteQueue(qname: string): Promise { await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}`, + url: `${getBaseUrl()}/queues/${qname}`, }); } export async function pauseQueue(qname: string): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}:pause`, + url: `${getBaseUrl()}/queues/${qname}:pause`, }); } export async function resumeQueue(qname: string): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}:resume`, + url: `${getBaseUrl()}/queues/${qname}:resume`, }); } export async function listQueueStats(): Promise { const resp = await axios({ method: "get", - url: `${BASE_URL}/queue_stats`, + url: `${getBaseUrl()}/queue_stats`, }); return resp.data; } @@ -378,7 +378,7 @@ export async function getTaskInfo( qname: string, id: string ): Promise { - const url = `${BASE_URL}/queues/${qname}/tasks/${id}`; + const url = `${getBaseUrl()}/queues/${qname}/tasks/${id}`; const resp = await axios({ method: "get", url, @@ -390,7 +390,7 @@ export async function listActiveTasks( qname: string, pageOpts?: PaginationOptions ): Promise { - let url = `${BASE_URL}/queues/${qname}/active_tasks`; + let url = `${getBaseUrl()}/queues/${qname}/active_tasks`; if (pageOpts) { url += `?${queryString.stringify(pageOpts)}`; } @@ -407,14 +407,14 @@ export async function cancelActiveTask( ): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/active_tasks/${taskId}:cancel`, + url: `${getBaseUrl()}/queues/${qname}/active_tasks/${taskId}:cancel`, }); } export async function cancelAllActiveTasks(qname: string): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/active_tasks:cancel_all`, + url: `${getBaseUrl()}/queues/${qname}/active_tasks:cancel_all`, }); } @@ -424,7 +424,7 @@ export async function batchCancelActiveTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/active_tasks:batch_cancel`, + url: `${getBaseUrl()}/queues/${qname}/active_tasks:batch_cancel`, data: { task_ids: taskIds, }, @@ -436,7 +436,7 @@ export async function listPendingTasks( qname: string, pageOpts?: PaginationOptions ): Promise { - let url = `${BASE_URL}/queues/${qname}/pending_tasks`; + let url = `${getBaseUrl()}/queues/${qname}/pending_tasks`; if (pageOpts) { url += `?${queryString.stringify(pageOpts)}`; } @@ -451,7 +451,7 @@ export async function listScheduledTasks( qname: string, pageOpts?: PaginationOptions ): Promise { - let url = `${BASE_URL}/queues/${qname}/scheduled_tasks`; + let url = `${getBaseUrl()}/queues/${qname}/scheduled_tasks`; if (pageOpts) { url += `?${queryString.stringify(pageOpts)}`; } @@ -466,7 +466,7 @@ export async function listRetryTasks( qname: string, pageOpts?: PaginationOptions ): Promise { - let url = `${BASE_URL}/queues/${qname}/retry_tasks`; + let url = `${getBaseUrl()}/queues/${qname}/retry_tasks`; if (pageOpts) { url += `?${queryString.stringify(pageOpts)}`; } @@ -481,7 +481,7 @@ export async function listArchivedTasks( qname: string, pageOpts?: PaginationOptions ): Promise { - let url = `${BASE_URL}/queues/${qname}/archived_tasks`; + let url = `${getBaseUrl()}/queues/${qname}/archived_tasks`; if (pageOpts) { url += `?${queryString.stringify(pageOpts)}`; } @@ -496,7 +496,7 @@ export async function listCompletedTasks( qname: string, pageOpts?: PaginationOptions ): Promise { - let url = `${BASE_URL}/queues/${qname}/completed_tasks`; + let url = `${getBaseUrl()}/queues/${qname}/completed_tasks`; if (pageOpts) { url += `?${queryString.stringify(pageOpts)}`; } @@ -513,7 +513,7 @@ export async function archivePendingTask( ): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/pending_tasks/${taskId}:archive`, + url: `${getBaseUrl()}/queues/${qname}/pending_tasks/${taskId}:archive`, }); } @@ -523,7 +523,7 @@ export async function batchArchivePendingTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/pending_tasks:batch_archive`, + url: `${getBaseUrl()}/queues/${qname}/pending_tasks:batch_archive`, data: { task_ids: taskIds, }, @@ -534,7 +534,7 @@ export async function batchArchivePendingTasks( export async function archiveAllPendingTasks(qname: string): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/pending_tasks:archive_all`, + url: `${getBaseUrl()}/queues/${qname}/pending_tasks:archive_all`, }); } @@ -544,7 +544,7 @@ export async function deletePendingTask( ): Promise { await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/pending_tasks/${taskId}`, + url: `${getBaseUrl()}/queues/${qname}/pending_tasks/${taskId}`, }); } @@ -554,7 +554,7 @@ export async function batchDeletePendingTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/pending_tasks:batch_delete`, + url: `${getBaseUrl()}/queues/${qname}/pending_tasks:batch_delete`, data: { task_ids: taskIds, }, @@ -567,7 +567,7 @@ export async function deleteAllPendingTasks( ): Promise { const resp = await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/pending_tasks:delete_all`, + url: `${getBaseUrl()}/queues/${qname}/pending_tasks:delete_all`, }); return resp.data; } @@ -578,7 +578,7 @@ export async function runScheduledTask( ): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/scheduled_tasks/${taskId}:run`, + url: `${getBaseUrl()}/queues/${qname}/scheduled_tasks/${taskId}:run`, }); } @@ -588,7 +588,7 @@ export async function archiveScheduledTask( ): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/scheduled_tasks/${taskId}:archive`, + url: `${getBaseUrl()}/queues/${qname}/scheduled_tasks/${taskId}:archive`, }); } @@ -598,7 +598,7 @@ export async function deleteScheduledTask( ): Promise { await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/scheduled_tasks/${taskId}`, + url: `${getBaseUrl()}/queues/${qname}/scheduled_tasks/${taskId}`, }); } @@ -608,7 +608,7 @@ export async function batchDeleteScheduledTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/scheduled_tasks:batch_delete`, + url: `${getBaseUrl()}/queues/${qname}/scheduled_tasks:batch_delete`, data: { task_ids: taskIds, }, @@ -621,7 +621,7 @@ export async function deleteAllScheduledTasks( ): Promise { const resp = await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/scheduled_tasks:delete_all`, + url: `${getBaseUrl()}/queues/${qname}/scheduled_tasks:delete_all`, }); return resp.data; } @@ -632,7 +632,7 @@ export async function batchRunScheduledTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/scheduled_tasks:batch_run`, + url: `${getBaseUrl()}/queues/${qname}/scheduled_tasks:batch_run`, data: { task_ids: taskIds, }, @@ -643,7 +643,7 @@ export async function batchRunScheduledTasks( export async function runAllScheduledTasks(qname: string): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/scheduled_tasks:run_all`, + url: `${getBaseUrl()}/queues/${qname}/scheduled_tasks:run_all`, }); } @@ -653,7 +653,7 @@ export async function batchArchiveScheduledTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/scheduled_tasks:batch_archive`, + url: `${getBaseUrl()}/queues/${qname}/scheduled_tasks:batch_archive`, data: { task_ids: taskIds, }, @@ -664,7 +664,7 @@ export async function batchArchiveScheduledTasks( export async function archiveAllScheduledTasks(qname: string): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/scheduled_tasks:archive_all`, + url: `${getBaseUrl()}/queues/${qname}/scheduled_tasks:archive_all`, }); } @@ -674,7 +674,7 @@ export async function runRetryTask( ): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/retry_tasks/${taskId}:run`, + url: `${getBaseUrl()}/queues/${qname}/retry_tasks/${taskId}:run`, }); } @@ -684,7 +684,7 @@ export async function archiveRetryTask( ): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/retry_tasks/${taskId}:archive`, + url: `${getBaseUrl()}/queues/${qname}/retry_tasks/${taskId}:archive`, }); } @@ -694,7 +694,7 @@ export async function deleteRetryTask( ): Promise { await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/retry_tasks/${taskId}`, + url: `${getBaseUrl()}/queues/${qname}/retry_tasks/${taskId}`, }); } @@ -704,7 +704,7 @@ export async function batchDeleteRetryTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/retry_tasks:batch_delete`, + url: `${getBaseUrl()}/queues/${qname}/retry_tasks:batch_delete`, data: { task_ids: taskIds, }, @@ -717,7 +717,7 @@ export async function deleteAllRetryTasks( ): Promise { const resp = await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/retry_tasks:delete_all`, + url: `${getBaseUrl()}/queues/${qname}/retry_tasks:delete_all`, }); return resp.data; } @@ -728,7 +728,7 @@ export async function batchRunRetryTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/retry_tasks:batch_run`, + url: `${getBaseUrl()}/queues/${qname}/retry_tasks:batch_run`, data: { task_ids: taskIds, }, @@ -739,7 +739,7 @@ export async function batchRunRetryTasks( export async function runAllRetryTasks(qname: string): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/retry_tasks:run_all`, + url: `${getBaseUrl()}/queues/${qname}/retry_tasks:run_all`, }); } @@ -749,7 +749,7 @@ export async function batchArchiveRetryTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/retry_tasks:batch_archive`, + url: `${getBaseUrl()}/queues/${qname}/retry_tasks:batch_archive`, data: { task_ids: taskIds, }, @@ -760,7 +760,7 @@ export async function batchArchiveRetryTasks( export async function archiveAllRetryTasks(qname: string): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/retry_tasks:archive_all`, + url: `${getBaseUrl()}/queues/${qname}/retry_tasks:archive_all`, }); } @@ -770,7 +770,7 @@ export async function runArchivedTask( ): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/archived_tasks/${taskId}:run`, + url: `${getBaseUrl()}/queues/${qname}/archived_tasks/${taskId}:run`, }); } @@ -780,7 +780,7 @@ export async function deleteArchivedTask( ): Promise { await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/archived_tasks/${taskId}`, + url: `${getBaseUrl()}/queues/${qname}/archived_tasks/${taskId}`, }); } @@ -790,7 +790,7 @@ export async function batchDeleteArchivedTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/archived_tasks:batch_delete`, + url: `${getBaseUrl()}/queues/${qname}/archived_tasks:batch_delete`, data: { task_ids: taskIds, }, @@ -803,7 +803,7 @@ export async function deleteAllArchivedTasks( ): Promise { const resp = await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/archived_tasks:delete_all`, + url: `${getBaseUrl()}/queues/${qname}/archived_tasks:delete_all`, }); return resp.data; } @@ -814,7 +814,7 @@ export async function batchRunArchivedTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/archived_tasks:batch_run`, + url: `${getBaseUrl()}/queues/${qname}/archived_tasks:batch_run`, data: { task_ids: taskIds, }, @@ -825,7 +825,7 @@ export async function batchRunArchivedTasks( export async function runAllArchivedTasks(qname: string): Promise { await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/archived_tasks:run_all`, + url: `${getBaseUrl()}/queues/${qname}/archived_tasks:run_all`, }); } @@ -835,7 +835,7 @@ export async function deleteCompletedTask( ): Promise { await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/completed_tasks/${taskId}`, + url: `${getBaseUrl()}/queues/${qname}/completed_tasks/${taskId}`, }); } @@ -845,7 +845,7 @@ export async function batchDeleteCompletedTasks( ): Promise { const resp = await axios({ method: "post", - url: `${BASE_URL}/queues/${qname}/completed_tasks:batch_delete`, + url: `${getBaseUrl()}/queues/${qname}/completed_tasks:batch_delete`, data: { task_ids: taskIds, }, @@ -858,7 +858,7 @@ export async function deleteAllCompletedTasks( ): Promise { const resp = await axios({ method: "delete", - url: `${BASE_URL}/queues/${qname}/completed_tasks:delete_all`, + url: `${getBaseUrl()}/queues/${qname}/completed_tasks:delete_all`, }); return resp.data; } @@ -866,7 +866,7 @@ export async function deleteAllCompletedTasks( export async function listServers(): Promise { const resp = await axios({ method: "get", - url: `${BASE_URL}/servers`, + url: `${getBaseUrl()}/servers`, }); return resp.data; } @@ -874,7 +874,7 @@ export async function listServers(): Promise { export async function listSchedulerEntries(): Promise { const resp = await axios({ method: "get", - url: `${BASE_URL}/scheduler_entries`, + url: `${getBaseUrl()}/scheduler_entries`, }); return resp.data; } @@ -884,7 +884,7 @@ export async function listSchedulerEnqueueEvents( ): Promise { const resp = await axios({ method: "get", - url: `${BASE_URL}/scheduler_entries/${entryId}/enqueue_events`, + url: `${getBaseUrl()}/scheduler_entries/${entryId}/enqueue_events`, }); return resp.data; } @@ -892,7 +892,7 @@ export async function listSchedulerEnqueueEvents( export async function getRedisInfo(): Promise { const resp = await axios({ method: "get", - url: `${BASE_URL}/redis_info`, + url: `${getBaseUrl()}/redis_info`, }); return resp.data; } @@ -917,7 +917,7 @@ export async function getMetrics( } const resp = await axios({ method: "get", - url: `${BASE_URL}/metrics?${queryString.stringify(params)}`, + url: `${getBaseUrl()}/metrics?${queryString.stringify(params)}`, }); return resp.data; } diff --git a/ui/src/components/QueueBreadcrumb.tsx b/ui/src/components/QueueBreadcrumb.tsx index 6146bf9..3fbbaba 100644 --- a/ui/src/components/QueueBreadcrumb.tsx +++ b/ui/src/components/QueueBreadcrumb.tsx @@ -6,7 +6,7 @@ import Chip from "@material-ui/core/Chip"; import Menu from "@material-ui/core/Menu"; import MenuItem from "@material-ui/core/MenuItem"; import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; -import { paths, queueDetailsPath } from "../paths"; +import { paths as getPaths, queueDetailsPath } from "../paths"; import { isDarkTheme } from "../theme"; const StyledBreadcrumb = withStyles((theme: Theme) => ({ @@ -39,6 +39,7 @@ interface Props { export default function QueueBreadcrumbs(props: Props) { const history = useHistory(); const [anchorEl, setAnchorEl] = useState(null); + const paths = getPaths(); const handleClick = (event: React.MouseEvent) => { event.preventDefault(); diff --git a/ui/src/global.d.ts b/ui/src/global.d.ts index 715ab74..cfb926a 100644 --- a/ui/src/global.d.ts +++ b/ui/src/global.d.ts @@ -1,4 +1,10 @@ interface Window { + // FLAG values are assigned by server under the window object. + // parseFlagsUnderWindow function parses these values and assigns the interpretted value under the window. + FLAG_ROOT_PATH: string; + FLAG_PROMETHEUS_SERVER_ADDRESS: string; + FLAG_READ_ONLY: string; + // Root URL path for asynqmon app. // ROOT_PATH should not have the tailing slash. ROOT_PATH: string; diff --git a/ui/src/index.tsx b/ui/src/index.tsx index 4d4e96c..72fa5de 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -4,10 +4,13 @@ import CssBaseline from "@material-ui/core/CssBaseline"; import { Provider } from "react-redux"; import App from "./App"; import store from "./store"; +import parseFlagsUnderWindow from "./parseFlags"; import * as serviceWorker from "./serviceWorker"; import { saveState } from "./localStorage"; import { SettingsState } from "./reducers/settingsReducer"; +parseFlagsUnderWindow(); + let currentSettings: SettingsState | undefined = undefined; store.subscribe(() => { const prevSettings = currentSettings; diff --git a/ui/src/parseFlags.ts b/ui/src/parseFlags.ts new file mode 100644 index 0000000..e1d67ea --- /dev/null +++ b/ui/src/parseFlags.ts @@ -0,0 +1,42 @@ +// Prefix used for go template +const goTmplActionPrefix = "/[["; + +// paseses flags (string values) assigned under the window objects by server. +export default function parseFlagsUnderWindow() { + // ROOT_PATH + if (window.FLAG_ROOT_PATH === undefined) { + console.log("ROOT_PATH is not defined. Falling back to emtpy string"); + window.ROOT_PATH = ""; + } else { + window.ROOT_PATH = window.FLAG_ROOT_PATH; + } + + // PROMETHEUS_SERVER_ADDRESS + if (window.FLAG_PROMETHEUS_SERVER_ADDRESS === undefined) { + console.log( + "PROMETHEUS_SERVER_ADDRESS is not defined. Falling back to emtpy string" + ); + window.PROMETHEUS_SERVER_ADDRESS = ""; + } else if ( + window.FLAG_PROMETHEUS_SERVER_ADDRESS.startsWith(goTmplActionPrefix) + ) { + console.log( + "PROMETHEUS_SERVER_ADDRESS was not evaluated by the server. Falling back to empty string" + ); + window.PROMETHEUS_SERVER_ADDRESS = ""; + } else { + } + + // READ_ONLY + if (window.FLAG_READ_ONLY === undefined) { + console.log("READ_ONLY is not defined. Falling back to false"); + window.READ_ONLY = false; + } else if (window.FLAG_READ_ONLY.startsWith(goTmplActionPrefix)) { + console.log( + "READ_ONLY was not evaluated by the server. Falling back to false" + ); + window.READ_ONLY = false; + } else { + window.READ_ONLY = window.FLAG_READ_ONLY === "true"; + } +} diff --git a/ui/src/paths.ts b/ui/src/paths.ts index a1fb41b..c374f7a 100644 --- a/ui/src/paths.ts +++ b/ui/src/paths.ts @@ -1,4 +1,4 @@ -export const paths = { +export const paths = () => ({ HOME: `${window.ROOT_PATH}/`, SETTINGS: `${window.ROOT_PATH}/settings`, SERVERS: `${window.ROOT_PATH}/servers`, @@ -7,14 +7,14 @@ export const paths = { REDIS: `${window.ROOT_PATH}/redis`, TASK_DETAILS: `${window.ROOT_PATH}/queues/:qname/tasks/:taskId`, QUEUE_METRICS: `${window.ROOT_PATH}/q/metrics`, -}; +}); /************************************************************** Path Helper functions **************************************************************/ export function queueDetailsPath(qname: string, taskStatus?: string): string { - const path = paths.QUEUE_DETAILS.replace(":qname", qname); + const path = paths().QUEUE_DETAILS.replace(":qname", qname); if (taskStatus) { return `${path}?status=${taskStatus}`; } @@ -22,7 +22,9 @@ export function queueDetailsPath(qname: string, taskStatus?: string): string { } export function taskDetailsPath(qname: string, taskId: string): string { - return paths.TASK_DETAILS.replace(":qname", qname).replace(":taskId", taskId); + return paths() + .TASK_DETAILS.replace(":qname", qname) + .replace(":taskId", taskId); } /**************************************************************