Added schedule overview to admin
This commit is contained in:
+61
-2
@@ -238,7 +238,7 @@ type AppInfo struct {
|
|||||||
type ScheduleOld struct {
|
type ScheduleOld struct {
|
||||||
Id string `json:"id" datastore:"id"`
|
Id string `json:"id" datastore:"id"`
|
||||||
Seconds int `json:"seconds" datastore:"seconds"`
|
Seconds int `json:"seconds" datastore:"seconds"`
|
||||||
WorkflowId string `json:"workflow_id datastore:"workflow_id", `
|
WorkflowId string `json:"workflow_id" datastore:"workflow_id", `
|
||||||
Argument string `json:"argument" datastore:"argument"`
|
Argument string `json:"argument" datastore:"argument"`
|
||||||
AppInfo AppInfo `json:"appinfo" datastore:"appinfo,noindex"`
|
AppInfo AppInfo `json:"appinfo" datastore:"appinfo,noindex"`
|
||||||
Finished bool `json:"finished" finished:"id"`
|
Finished bool `json:"finished" finished:"id"`
|
||||||
@@ -1803,6 +1803,49 @@ func getUserCount() (int, error) {
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleGetSchedules(resp http.ResponseWriter, request *http.Request) {
|
||||||
|
cors := handleCors(resp, request)
|
||||||
|
if cors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := handleApiAuthentication(resp, request)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Api authentication failed in set new workflowhandler: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Role != "admin" {
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Admin required"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
schedules, err := getAllSchedules(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed getting schedules: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Couldn't get schedules"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newjson, err := json.Marshal(schedules)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed unmarshal: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking environments"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//log.Printf("Existing environments: %s", string(newjson))
|
||||||
|
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write(newjson)
|
||||||
|
}
|
||||||
|
|
||||||
func handleGetEnvironments(resp http.ResponseWriter, request *http.Request) {
|
func handleGetEnvironments(resp http.ResponseWriter, request *http.Request) {
|
||||||
cors := handleCors(resp, request)
|
cors := handleCors(resp, request)
|
||||||
if cors {
|
if cors {
|
||||||
@@ -2635,6 +2678,21 @@ func handleDeleteSchedule(resp http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user, err := handleApiAuthentication(resp, request)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Api authentication failed in set new workflowhandler: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: IAM - Get workflow and check owner
|
||||||
|
if user.Role != "admin" {
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Admin required"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
location := strings.Split(request.URL.String(), "/")
|
location := strings.Split(request.URL.String(), "/")
|
||||||
|
|
||||||
var workflowId string
|
var workflowId string
|
||||||
@@ -2655,7 +2713,7 @@ func handleDeleteSchedule(resp http.ResponseWriter, request *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
err := DeleteKey(ctx, "schedules", workflowId)
|
err = DeleteKey(ctx, "schedules", workflowId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.WriteHeader(401)
|
resp.WriteHeader(401)
|
||||||
resp.Write([]byte(`{"success": false, "message": "Can't delete"}`))
|
resp.Write([]byte(`{"success": false, "message": "Can't delete"}`))
|
||||||
@@ -5914,6 +5972,7 @@ func init() {
|
|||||||
/* Everything below here increases the counters*/
|
/* Everything below here increases the counters*/
|
||||||
r.HandleFunc("/api/v1/workflows", getWorkflows).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/workflows", getWorkflows).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/workflows", setNewWorkflow).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/workflows", setNewWorkflow).Methods("POST", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/workflows/schedules", handleGetSchedules).Methods("GET", "OPTIONS")
|
||||||
//r.HandleFunc("/api/v1/workflows/{key}/execute_fs", executeWorkflowFS)
|
//r.HandleFunc("/api/v1/workflows/{key}/execute_fs", executeWorkflowFS)
|
||||||
r.HandleFunc("/api/v1/workflows/{key}/execute", executeWorkflow).Methods("GET", "POST", "OPTIONS")
|
r.HandleFunc("/api/v1/workflows/{key}/execute", executeWorkflow).Methods("GET", "POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/workflows/{key}/schedule", scheduleWorkflow).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/workflows/{key}/schedule", scheduleWorkflow).Methods("POST", "OPTIONS")
|
||||||
|
|||||||
@@ -2021,6 +2021,7 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func stopSchedule(resp http.ResponseWriter, request *http.Request) {
|
func stopSchedule(resp http.ResponseWriter, request *http.Request) {
|
||||||
|
log.Printf("Delete?")
|
||||||
cors := handleCors(resp, request)
|
cors := handleCors(resp, request)
|
||||||
if cors {
|
if cors {
|
||||||
return
|
return
|
||||||
@@ -2079,19 +2080,6 @@ func stopSchedule(resp http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(workflow.Actions) == 0 {
|
|
||||||
workflow.Actions = []Action{}
|
|
||||||
}
|
|
||||||
if len(workflow.Branches) == 0 {
|
|
||||||
workflow.Branches = []Branch{}
|
|
||||||
}
|
|
||||||
if len(workflow.Triggers) == 0 {
|
|
||||||
workflow.Triggers = []Trigger{}
|
|
||||||
}
|
|
||||||
if len(workflow.Errors) == 0 {
|
|
||||||
workflow.Errors = []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = deleteSchedule(ctx, scheduleId)
|
err = deleteSchedule(ctx, scheduleId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "Job not found") {
|
if strings.Contains(err.Error(), "Job not found") {
|
||||||
|
|||||||
+109
-1
@@ -8,7 +8,8 @@ import ListItem from '@material-ui/core/ListItem';
|
|||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import Tabs from '@material-ui/core/Tabs';
|
import Tabs from '@material-ui/core/Tabs';
|
||||||
import Tab from '@material-ui/core/Tab';
|
import Tab from '@material-ui/core/Tab';
|
||||||
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
|
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
|
||||||
|
|
||||||
import { useAlert } from "react-alert";
|
import { useAlert } from "react-alert";
|
||||||
|
|
||||||
@@ -29,9 +30,39 @@ const Admin = (props) => {
|
|||||||
const [curTab, setCurTab] = React.useState(0);
|
const [curTab, setCurTab] = React.useState(0);
|
||||||
const [users, setUsers] = React.useState([]);
|
const [users, setUsers] = React.useState([]);
|
||||||
const [environments, setEnvironments] = React.useState([]);
|
const [environments, setEnvironments] = React.useState([]);
|
||||||
|
const [schedules, setSchedules] = React.useState([])
|
||||||
|
|
||||||
const alert = useAlert()
|
const alert = useAlert()
|
||||||
|
|
||||||
|
const deleteSchedule = (data) => {
|
||||||
|
// FIXME - add some check here ROFL
|
||||||
|
console.log("INPUT: ", data)
|
||||||
|
|
||||||
|
// Just use this one?
|
||||||
|
const url = globalUrl+'/api/v1/workflows/'+data["workflow_id datastore:"]+"/schedule/"+data.id
|
||||||
|
console.log("URL: ", url)
|
||||||
|
fetch(url, {
|
||||||
|
method: 'DELETE',
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(response =>
|
||||||
|
response.json().then(responseJson => {
|
||||||
|
if (responseJson["success"] === false) {
|
||||||
|
alert.error("Failed stopping schedule")
|
||||||
|
} else {
|
||||||
|
getSchedules()
|
||||||
|
alert.success("Successfully stopped schedule!")
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.catch(error => {
|
||||||
|
console.log("Error in userdata: ", error)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const submitUser = (data) => {
|
const submitUser = (data) => {
|
||||||
// FIXME - add some check here ROFL
|
// FIXME - add some check here ROFL
|
||||||
console.log("INPUT: ", data)
|
console.log("INPUT: ", data)
|
||||||
@@ -132,6 +163,31 @@ const Admin = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSchedules = () => {
|
||||||
|
fetch(globalUrl+"/api/v1/workflows/schedules", {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for apps :O!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
setSchedules(responseJson)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert.error(error.toString())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const getEnvironments = () => {
|
const getEnvironments = () => {
|
||||||
fetch(globalUrl+"/api/v1/getenvironments", {
|
fetch(globalUrl+"/api/v1/getenvironments", {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -329,6 +385,54 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
|
const schedulesView = curTab === 2 ?
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
Schedules
|
||||||
|
</h2>
|
||||||
|
<Divider style={{marginTop: 20, marginBottom: 20, backgroundColor: inputColor}}/>
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText
|
||||||
|
primary="Interval (seconds)"
|
||||||
|
style={{maxWidth: 200}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Argument"
|
||||||
|
style={{maxWidth: 400, overflow: "hidden"}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Actions"
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
{schedules === undefined || schedules === null ? null : schedules.map(schedule => {
|
||||||
|
return (
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText
|
||||||
|
style={{maxWidth: 200}}
|
||||||
|
primary={schedule.seconds}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={schedule.argument}
|
||||||
|
style={{maxWidth: 400, overflow: "hidden"}}
|
||||||
|
/>
|
||||||
|
<ListItemText>
|
||||||
|
<Button
|
||||||
|
style={{}}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => deleteSchedule(schedule)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</ListItemText>
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
|
||||||
const environmentView = curTab === 1 ?
|
const environmentView = curTab === 1 ?
|
||||||
<div>
|
<div>
|
||||||
<h2>
|
<h2>
|
||||||
@@ -359,6 +463,8 @@ const Admin = (props) => {
|
|||||||
const setConfig = (event, newValue) => {
|
const setConfig = (event, newValue) => {
|
||||||
if (newValue === 1) {
|
if (newValue === 1) {
|
||||||
getEnvironments()
|
getEnvironments()
|
||||||
|
} else if (newValue === 2) {
|
||||||
|
getSchedules()
|
||||||
}
|
}
|
||||||
|
|
||||||
setModalUser({})
|
setModalUser({})
|
||||||
@@ -377,10 +483,12 @@ const Admin = (props) => {
|
|||||||
>
|
>
|
||||||
<Tab label="Users" />
|
<Tab label="Users" />
|
||||||
<Tab label="Environments"/>
|
<Tab label="Environments"/>
|
||||||
|
<Tab label="Schedules"/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<div style={{marginBottom: 10}}/>
|
<div style={{marginBottom: 10}}/>
|
||||||
{usersView}
|
{usersView}
|
||||||
{environmentView}
|
{environmentView}
|
||||||
|
{schedulesView}
|
||||||
</Paper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1588,14 +1588,14 @@ const AppCreator = (props) => {
|
|||||||
{bearerAuth}
|
{bearerAuth}
|
||||||
{apiKey}
|
{apiKey}
|
||||||
|
|
||||||
{authenticationOption === "No authentication" ? null :
|
{/*authenticationOption === "No authentication" ? null :
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
style={{color: "white", marginBottom: 0, marginTop: 20}}
|
style={{color: "white", marginBottom: 0, marginTop: 20}}
|
||||||
label=<div style={{color: "white"}}>Authentication required (default true)</div>
|
label=<div style={{color: "white"}}>Authentication required (default true)</div>
|
||||||
control={<Switch checked={authenticationRequired} onChange={() => {
|
control={<Switch checked={authenticationRequired} onChange={() => {
|
||||||
setAuthenticationRequired(!authenticationRequired)
|
setAuthenticationRequired(!authenticationRequired)
|
||||||
}} />}
|
}} />}
|
||||||
/>}
|
/>*/}
|
||||||
<Divider style={{marginBottom: "10px", marginTop: "30px", height: "1px", width: "100%", backgroundColor: "grey"}}/>
|
<Divider style={{marginBottom: "10px", marginTop: "30px", height: "1px", width: "100%", backgroundColor: "grey"}}/>
|
||||||
<div style={{marginTop: "25px"}}>
|
<div style={{marginTop: "25px"}}>
|
||||||
{actionView}
|
{actionView}
|
||||||
|
|||||||
Reference in New Issue
Block a user