You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
16 KiB
373 lines
16 KiB
import styles from './styles.module.css'
|
|
import {useContext, useEffect, useState} from "react"
|
|
import DeleteForeverOutlined from "@mui/icons-material/DeleteForeverOutlined"
|
|
import StopCircleOutlined from "@mui/icons-material/StopCircleOutlined"
|
|
import PauseCircleOutline from "@mui/icons-material/PauseCircleOutline"
|
|
import PlayCircleOutline from "@mui/icons-material/PlayCircleOutline"
|
|
import HourglassEmptyOutlined from "@mui/icons-material/HourglassEmptyOutlined"
|
|
import CheckCircleOutline from "@mui/icons-material/CheckCircleOutline"
|
|
import ErrorOutline from "@mui/icons-material/ErrorOutline"
|
|
import ReplayOutlined from "@mui/icons-material/ReplayOutlined"
|
|
import RunCircleOutlined from "@mui/icons-material/RunCircleOutlined"
|
|
import FactCheckOutlined from "@mui/icons-material/FactCheckOutlined"
|
|
import ThumbUpAltOutlined from '@mui/icons-material/ThumbUpAltOutlined'
|
|
import LockOutlined from '@mui/icons-material/LockOutlined';
|
|
import LockOpenOutlined from '@mui/icons-material/LockOpenOutlined';
|
|
|
|
import {
|
|
Box,
|
|
TextField,
|
|
CircularProgress,
|
|
IconButton,
|
|
LinearProgress,
|
|
TableContainer,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableRow,
|
|
TablePagination, Autocomplete
|
|
} from "@mui/material"
|
|
import ConfirmDialog from "../confirm-dialog";
|
|
import {ProcessInterface, Status} from "../../../api/sm/responses/processes";
|
|
import Command from "../commands/elements/command";
|
|
import {CommandInterface} from "../../../api/sm/responses/comamnds";
|
|
import Grid from '@mui/material/Grid';
|
|
import {useApi} from "../../../hooks/use-api";
|
|
import Context from "../../../context/token";
|
|
|
|
enum Action {
|
|
Run,
|
|
Repeat,
|
|
Stop,
|
|
Kill,
|
|
Play,
|
|
Pause,
|
|
Approve,
|
|
Unlock,
|
|
}
|
|
|
|
export default function Processes() {
|
|
const {token} = useContext(Context)
|
|
const [processes, setProcesses] = useState<ProcessInterface[]>([]);
|
|
const [commands, setCommands] = useState<CommandInterface[]>([]);
|
|
const [page, setPage] = useState<number>(0);
|
|
const [count, setCount] = useState<number>(0);
|
|
const [type, setType] = useState<string>('');
|
|
const [name, setName] = useState<string>('');
|
|
const [loading, setLoading] = useState<boolean>(true);
|
|
const [modalLoading, setModalLoading] = useState<boolean>(true);
|
|
const [action, setAction] = useState<Action | null>(null);
|
|
const [command, setCommand] = useState<CommandInterface | null>(null);
|
|
const [selectedProcess, setSelectedProcess] = useState<ProcessInterface | null>(null);
|
|
const [optionList, setOptionList] = useState<Record<string, any>>({});
|
|
const [argumentList, setArgumentList] = useState<Record<string, any>>({});
|
|
const api = useApi()
|
|
|
|
let variants = commands.map((command: CommandInterface, index: number) => command.name);
|
|
variants.push('')
|
|
|
|
let refreshCommands = async () => {
|
|
const { data: commands } = await api.useMemory().getCommands()
|
|
setCommands(commands)
|
|
}
|
|
|
|
useEffect(() => {
|
|
refreshCommands()
|
|
}, [])
|
|
|
|
let refreshLock = false
|
|
let refreshProcesses = async () => {
|
|
if (refreshLock) {
|
|
return
|
|
}
|
|
refreshLock = true
|
|
|
|
let data: any = {}
|
|
if (type && type !== 'None') {
|
|
data['type'] = type.toLowerCase()
|
|
}
|
|
if (!!name) {
|
|
data['name'] = name
|
|
}
|
|
const { data: processes, headers } = await api.getProcesses({
|
|
...data,
|
|
page: page + 1,
|
|
limit: 20,
|
|
})
|
|
setProcesses(processes)
|
|
setCount(Number(headers.get('X-Pagination-Count')))
|
|
setLoading(false)
|
|
refreshLock = false
|
|
}
|
|
let output = async (process: ProcessInterface) => {
|
|
const { data: output } = await api.getProcessOutput({
|
|
id: process.id
|
|
})
|
|
|
|
let a = document.createElement("a");
|
|
let file = new Blob([output], {type: 'plain/text'});
|
|
a.href = URL.createObjectURL(file);
|
|
a.download = `${process.id}.txt`;
|
|
a.click();
|
|
}
|
|
|
|
useEffect(() => {
|
|
const timer = setInterval(() => refreshProcesses(), 1000)
|
|
return () => clearInterval(timer);
|
|
}, [page, name, type]);
|
|
|
|
useEffect(() => {
|
|
if (action === null) {
|
|
setSelectedProcess(null)
|
|
setCommand(null)
|
|
}
|
|
}, [action]);
|
|
|
|
let isFinished = (process: ProcessInterface) => process.cancelledAt || process.completedAt
|
|
let isCancelled = (process: ProcessInterface) => process.cancelledAt
|
|
let isRunning = (process: ProcessInterface) => process.startedAt && !isFinished(process)
|
|
|
|
const processName = (process: ProcessInterface) => {
|
|
return process.name + ' ' + JSON.stringify(process.options)+ ' ' + JSON.stringify(process.arguments)
|
|
}
|
|
|
|
const handleChangePage = (event: any, page: number) => {
|
|
setPage(page);
|
|
}
|
|
|
|
const openDialog = (process: ProcessInterface, action: Action) => {
|
|
setSelectedProcess(process)
|
|
setAction(action)
|
|
}
|
|
|
|
let lock = false
|
|
const agreeCallback = async (dialogId: string) => {
|
|
if (lock) {
|
|
return
|
|
}
|
|
if (!selectedProcess) {
|
|
return
|
|
}
|
|
lock = true
|
|
|
|
if (action === Action.Run) {
|
|
await api.runCommand({
|
|
commandName: selectedProcess.name,
|
|
options: optionList,
|
|
arguments: argumentList,
|
|
requestId: dialogId,
|
|
})
|
|
}
|
|
|
|
if (action === Action.Approve) {
|
|
await api.approveProcess({
|
|
id: selectedProcess.id,
|
|
})
|
|
}
|
|
|
|
if (action === Action.Unlock) {
|
|
await api.unlockProcess({
|
|
id: selectedProcess.id,
|
|
})
|
|
}
|
|
|
|
if (action === Action.Repeat) {
|
|
await api.repeatProcess({
|
|
id: selectedProcess.id,
|
|
requestId: dialogId
|
|
})
|
|
}
|
|
|
|
lock = false
|
|
setAction(null)
|
|
}
|
|
|
|
let callback = (name: string, optionList: Record<string, any>, argumentList: Record<string, any>) => {
|
|
setOptionList(optionList)
|
|
setArgumentList(argumentList)
|
|
}
|
|
|
|
if (loading) {
|
|
return (
|
|
<Box sx={{ display: 'flex', marginTop: '200px' }}>
|
|
<CircularProgress />
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<br/>
|
|
<Grid container>
|
|
<Grid item xs={1}>
|
|
<Autocomplete
|
|
value={type}
|
|
onChange={(event: any, newValue: string | null) => {
|
|
setType(newValue || '');
|
|
setLoading(true)
|
|
}}
|
|
disablePortal
|
|
options={['None', "Running", "History", '']}
|
|
renderInput={(params) => <TextField {...params} label="Type"/>}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={3}>
|
|
<Autocomplete
|
|
sx={{marginLeft: 5}}
|
|
value={name}
|
|
onChange={(event: any, newValue: string | null) => {
|
|
setName(newValue || '');
|
|
setLoading(true)
|
|
}}
|
|
disablePortal
|
|
options={variants}
|
|
renderInput={(params) => <TextField {...params} label="Commands"/>}
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
<TableContainer>
|
|
<Table>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell>Name</TableCell>
|
|
<TableCell>Progress</TableCell>
|
|
<TableCell>Status</TableCell>
|
|
<TableCell>Action</TableCell>
|
|
<TableCell>Created</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{processes.map((process: ProcessInterface, index: number) => (
|
|
<TableRow key={process.id}>
|
|
<TableCell title={processName(process)}>{process.name}</TableCell>
|
|
<TableCell>
|
|
{!process.progress && !isFinished(process) && isRunning(process) && <LinearProgress/>}
|
|
{!process.progress && !isFinished(process) && !isRunning(process) && <LinearProgress variant="determinate" value={0}/>}
|
|
{!process.progress && isFinished(process) && !isCancelled(process) && <LinearProgress variant="determinate" value={100}/>}
|
|
{!process.progress && isFinished(process) && isCancelled(process) && <LinearProgress variant="determinate" value={0}/>}
|
|
{process.progress && <LinearProgress variant="determinate" value={process.progress.percent}/>}
|
|
{process.progress && <span>
|
|
{`${process.progress.progress}`} / {`${process.progress.total}`} - {process.progress.percent}% [{process.progress.memory}] / {process.progress.remaining}
|
|
</span>}
|
|
{process.canPlay && <IconButton onClick={() => openDialog(process, Action.Play)} title={`Play`} aria-label="Play">
|
|
<PlayCircleOutline/>
|
|
</IconButton>}
|
|
{process.canPause && <IconButton onClick={() => openDialog(process, Action.Pause)} title={`Pause`} aria-label="Pause">
|
|
<PauseCircleOutline/>
|
|
</IconButton>}
|
|
{process.canStop && <IconButton onClick={() => openDialog(process, Action.Stop)} title={`Stop`} aria-label="Stop">
|
|
<StopCircleOutlined/>
|
|
</IconButton>}
|
|
</TableCell>
|
|
<TableCell>
|
|
{Status.Error === process.status && <IconButton title={`Error`} aria-label="Error">
|
|
<ErrorOutline/>
|
|
</IconButton>}
|
|
{Status.Success === process.status && <IconButton title={`Success`} aria-label="Success">
|
|
<CheckCircleOutline/>
|
|
</IconButton>}
|
|
{Status.Running === process.status && <IconButton title={`Running`} aria-label="Running">
|
|
<RunCircleOutlined/>
|
|
</IconButton>}
|
|
{Status.Cancelled === process.status && <IconButton title={`Cancelled`} aria-label="Cancelled">
|
|
<StopCircleOutlined/>
|
|
</IconButton>}
|
|
{Status.Wait === process.status && <IconButton title={`Wait`} aria-label="Wait">
|
|
<HourglassEmptyOutlined/>
|
|
</IconButton>}
|
|
{process.lock && <IconButton title={`Locked`} aria-label="Locked">
|
|
<LockOutlined/>
|
|
</IconButton>}
|
|
</TableCell>
|
|
<TableCell>
|
|
{process.canUnlock && token && token?.permissions.indexOf('unlock_process') > -1 && <IconButton onClick={() => openDialog(process, Action.Unlock)} title={`Unlock`} aria-label="Unlock">
|
|
<LockOpenOutlined/>
|
|
</IconButton>}
|
|
{process.canApprove && token && token?.permissions.indexOf('approve_process') > -1 && <IconButton onClick={() => openDialog(process, Action.Approve)} title={`Approve`} aria-label="Approve">
|
|
<ThumbUpAltOutlined/>
|
|
</IconButton>}
|
|
{process.canRepeat && token && token?.permissions.indexOf('run_command') > -1 && <IconButton onClick={() => openDialog(process, Action.Repeat)} title={`Repeat`} aria-label="Repeat">
|
|
<ReplayOutlined/>
|
|
</IconButton>}
|
|
{process.canKill && token && token?.permissions.indexOf('kill_process') > -1 && <IconButton onClick={() => openDialog(process, Action.Kill)} title={`Kill`} aria-label="Kill">
|
|
<DeleteForeverOutlined/>
|
|
</IconButton>}
|
|
{process?.outputId && <IconButton title={`Output`} onClick={() => output(process)} aria-label="Output">
|
|
<FactCheckOutlined/>
|
|
</IconButton>}
|
|
</TableCell>
|
|
<TableCell title={new Date(process.createdAt).toLocaleString()}>
|
|
{new Date(process.createdAt).toLocaleString()}
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
<TablePagination
|
|
component="div"
|
|
count={count}
|
|
rowsPerPage={20}
|
|
rowsPerPageOptions={[10, 20, 50, 100]}
|
|
page={page}
|
|
onPageChange={handleChangePage}
|
|
/>
|
|
{selectedProcess && <ConfirmDialog
|
|
title={selectedProcess.name}
|
|
open={Action.Repeat === action}
|
|
agreeCallback={agreeCallback}
|
|
modifyCallback={async () => {
|
|
setAction(Action.Run)
|
|
setModalLoading(true)
|
|
let {data: command} = await api.getCommand(selectedProcess.name)
|
|
setCommand(command)
|
|
setModalLoading(false)
|
|
}}
|
|
closeCallback={() => {setAction(null)}}>
|
|
Reply?
|
|
</ConfirmDialog>}
|
|
{selectedProcess && <ConfirmDialog
|
|
title={selectedProcess.name}
|
|
open={Action.Run === action}
|
|
agreeCallback={agreeCallback}
|
|
closeCallback={() => {setAction(null)}}>
|
|
{modalLoading && <Box sx={{ display: 'flex' }}>
|
|
<CircularProgress />
|
|
</Box>}
|
|
{!modalLoading && !!command && (Action.Run === action) && <Command
|
|
command={command}
|
|
optionsParams={selectedProcess.options}
|
|
argumentsParams={selectedProcess.arguments}
|
|
callback={callback}/>}
|
|
</ConfirmDialog>}
|
|
{selectedProcess && <ConfirmDialog
|
|
title={selectedProcess.name}
|
|
open={Action.Approve === action}
|
|
agreeCallback={agreeCallback}
|
|
closeCallback={() => {setAction(null)}}>
|
|
Approve?
|
|
</ConfirmDialog>}
|
|
{selectedProcess && <ConfirmDialog
|
|
title={selectedProcess.name}
|
|
open={Action.Unlock === action}
|
|
agreeCallback={agreeCallback}
|
|
closeCallback={() => {setAction(null)}}>
|
|
Unlock?
|
|
</ConfirmDialog>}
|
|
{selectedProcess && <ConfirmDialog
|
|
title={`Are you sure?`}
|
|
open={ !!action && ([Action.Play, Action.Pause, Action.Stop, Action.Kill].indexOf(action) > -1) }
|
|
agreeCallback={async () => {
|
|
Action.Play === action && await api.playProcess(selectedProcess.id)
|
|
Action.Pause === action && await api.pauseProcess(selectedProcess.id)
|
|
Action.Stop === action && await api.stopProcess(selectedProcess.id)
|
|
Action.Kill === action && await api.killProcess(selectedProcess.id)
|
|
setAction(null)
|
|
}}
|
|
closeCallback={() => {setAction(null)}}>
|
|
{selectedProcess.name}
|
|
</ConfirmDialog>}
|
|
</>
|
|
)
|
|
}
|
|
|