Added further admin panel fixes

This commit is contained in:
Frikky
2023-09-27 21:25:45 +02:00
parent 29e8106670
commit 787ca47add
12 changed files with 474 additions and 154 deletions
+15 -9
View File
@@ -1,11 +1,12 @@
import os import os
import ast import ast
import copy
import sys import sys
import re import re
import copy
import time import time
import base64 import base64
import json import json
import random
import liquid import liquid
import logging import logging
import urllib3 import urllib3
@@ -504,10 +505,12 @@ class AppBase:
except Exception as e: except Exception as e:
print(f"[WARNING] Failed adding parameter for logs: {e}") print(f"[WARNING] Failed adding parameter for logs: {e}")
# FIXME: Adding retries here.
try: try:
finished = False finished = False
for i in range (0, 10): for i in range (0, 10):
# Random sleeptime between 0 and 1 second, with 0.1 increments
sleeptime = float(random.randint(0, 10) / 10)
try: try:
ret = requests.post(url, headers=headers, json=action_result, timeout=10, verify=False) ret = requests.post(url, headers=headers, json=action_result, timeout=10, verify=False)
@@ -516,29 +519,29 @@ class AppBase:
finished = True finished = True
break break
else: else:
self.logger.info(f"[ERROR] RESP: {ret.text}") self.logger.info(f"[ERROR] Bad resp {ret.status_code}: {ret.text}")
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
self.logger.info(f"[DEBUG] Request problem: {e}") self.logger.info(f"[DEBUG] Request problem: {e}")
time.sleep(0.1) time.sleep(sleeptime)
#time.sleep(5) #time.sleep(5)
continue continue
except TimeoutError as e: except TimeoutError as e:
self.logger.info(f"[DEBUG] Timeout or request: {e}") self.logger.info(f"[DEBUG] Timeout or request: {e}")
time.sleep(0.1) time.sleep(sleeptime)
#time.sleep(5) #time.sleep(5)
continue continue
except requests.exceptions.ConnectionError as e: except requests.exceptions.ConnectionError as e:
self.logger.info(f"[DEBUG] Connectionerror: {e}") self.logger.info(f"[DEBUG] Connectionerror: {e}")
time.sleep(0.1) time.sleep(sleeptime)
#time.sleep(5) #time.sleep(5)
continue continue
except http.client.RemoteDisconnected as e: except http.client.RemoteDisconnected as e:
self.logger.info(f"[DEBUG] Remote: {e}") self.logger.info(f"[DEBUG] Remote: {e}")
time.sleep(0.1) time.sleep(sleeptime)
#time.sleep(5) #time.sleep(5)
continue continue
@@ -555,8 +558,11 @@ class AppBase:
# Not sure why this would work tho :) # Not sure why this would work tho :)
action_result["status"] = "FAILURE" action_result["status"] = "FAILURE"
action_result["result"] = json.dumps({"success": False, "reason": "POST error: Failed connecting to %s over 10 retries to the backend" % url}) action_result["result"] = json.dumps({"success": False, "reason": "POST error: Failed connecting to %s over 10 retries to the backend" % url})
self.logger.info(f"[DEBUG] Before typeerror stream result - NOT finished after 10 requests") self.logger.info(f"[ERROR] Before typeerror stream result - NOT finished after 10 requests")
ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result, verify=False)
#ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result, verify=False)
self.send_result(action_result, {"Content-Type": "application/json", "Authorization": "Bearer %s" % self.authorization}, "/api/v1/streams")
return
self.logger.info(f"""[DEBUG] Successful request result request: Status= {ret.status_code} & Response= {ret.text}. Action status: {action_result["status"]}""") self.logger.info(f"""[DEBUG] Successful request result request: Status= {ret.status_code} & Response= {ret.text}. Action status: {action_result["status"]}""")
except requests.exceptions.ConnectionError as e: except requests.exceptions.ConnectionError as e:
+2 -2
View File
@@ -1,6 +1,6 @@
module shuffle-shared module shuffle-shared
replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared //replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared
go 1.19 go 1.19
@@ -19,7 +19,7 @@ require (
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/h2non/filetype v1.1.3 github.com/h2non/filetype v1.1.3
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.4.41 github.com/shuffle/shuffle-shared v0.4.47
golang.org/x/crypto v0.9.0 golang.org/x/crypto v0.9.0
google.golang.org/api v0.125.0 google.golang.org/api v0.125.0
google.golang.org/appengine v1.6.7 google.golang.org/appengine v1.6.7
+2 -47
View File
@@ -22,10 +22,9 @@ import {
import aa from 'search-insights' import aa from 'search-insights'
const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240") const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e5240")
const Appsearch = props => { const Appsearch = props => {
const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList} = props const { maxRows, showName, showSuggestion, isMobile, globalUrl, parsedXs, newSelectedApp, setNewSelectedApp, defaultSearch, showSearch, ConfiguredHits, userdata, cy, isCreatorPage, actionImageList, setActionImageList, setUserSpecialzedApp } = props
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
//const alert = useAlert();
const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows const rowHandler = maxRows === undefined || maxRows === null ? 50 : maxRows
const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs const xs = parsedXs === undefined || parsedXs === null ? 12 : parsedXs
//const theme = useTheme(); //const theme = useTheme();
@@ -41,45 +40,6 @@ const Appsearch = props => {
const borderRadius = 3 const borderRadius = 3
window.title = "Shuffle | Apps | Find and integration any app" window.title = "Shuffle | Apps | Find and integration any app"
const setUserSpecialzedApp = (user, data) => {
// var data = newfields]
console.log("data value", data)
const appData = {"user_id":user,"specialized_apps":[{}]}
console.log("User Check for appdata:", user)
appData["specialized_apps"][0]["name"] = data["name"]
appData["specialized_apps"][0]["image"] = data["image_url"]
appData["specialized_apps"][0]["category"] = data["categories"].toString()
console.log("AppData:",appData)
console.log("setActionImageList",setActionImageList)
console.log("actionImageList",actionImageList)
const finalData = actionImageList.concat(appData["specialized_apps"])
appData["specialized_apps"]=finalData
fetch(globalUrl + "/api/v1/users/updateuser", {
method: "PUT",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(appData),
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for set creator :O!");
}
toast("Sucessfully updated specialzed app.")
return response.json();
})
.then((responseJson) => {
if (!responseJson.success && responseJson.reason !== undefined) {
toast("Failed updating user: " + responseJson.reason);
}
})
.catch((error) => {
console.log(error);
});
};
// value={currentRefinement} // value={currentRefinement}
const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => { const SearchBox = ({currentRefinement, refine, isSearchStalled} ) => {
@@ -180,13 +140,8 @@ const Appsearch = props => {
setMouseHoverIndex(-1) setMouseHoverIndex(-1)
}} onClick={() => { }} onClick={() => {
if(isCreatorPage === true){ if(isCreatorPage === true){
console.log("data:",data) if (setNewSelectedApp !== undefined && setUserSpecialzedApp !== undefined) {
console.log("userdata.id",userdata.id)
console.log("is creator", isCreatorPage)
if (setNewSelectedApp !== undefined) {
// setUserSpecialzedApp = data
setUserSpecialzedApp(userdata.id, data) setUserSpecialzedApp(userdata.id, data)
//setActionImageList(userdata.id, data)
} }
} }
if (setNewSelectedApp !== undefined) { if (setNewSelectedApp !== undefined) {
+105 -21
View File
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import ReactGA from 'react-ga4'; import ReactGA from 'react-ga4';
import theme from "../theme.jsx"; import theme from "../theme.jsx";
import { ToastContainer, toast } from "react-toastify"
import { import {
Paper, Paper,
@@ -14,22 +15,87 @@ import {
//import { useAlert //import { useAlert
const Branding = (props) => { const Branding = (props) => {
const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props; const { globalUrl, userdata, serverside, billingInfo, stripeKey, selectedOrganization, handleGetOrg, } = props;
//const alert = useAlert(); //const alert = useAlert();
const [publishingInfo, setPublishingInfo] = useState(""); const [publishingInfo, setPublishingInfo] = useState("");
const [publishRequirements, setPublishRequirements] = useState([])
// Should enable / disable org branding
const handleChangePublishing = () => { const handleEditOrg = (joinStatus) => {
console.log("Handle change publishing"); const data = {
} "org_id": selectedOrganization.id,
"creator_config": joinStatus,
};
const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
fetch(url, {
mode: "cors",
method: "POST",
body: JSON.stringify(data),
credentials: "include",
crossDomain: true,
withCredentials: true,
headers: {
"Content-Type": "application/json; charset=utf-8",
},
})
.then((response) =>
response.json().then((responseJson) => {
if (responseJson["success"] === false) {
toast("Failed updating org: ", responseJson.reason);
} else {
if (joinStatus == "join") {
setPublishingInfo("Your organization is now part of the Creator Incentive Program. You can now create and publish content to your organization's page. You can also create a creator account to manage your organization's content.")
} else {
setPublishingInfo("Your organization is no longer part of the Creator Incentive Program. You can still create a creator account to manage your organization's content.")
}
handleGetOrg(selectedOrganization.id);
}
})
)
.catch((error) => {
toast("Err: " + error.toString());
});
};
// Should enable / disable org branding
const handleChangePublishing = () => {
console.log("Handle change publishing");
if (selectedOrganization.creator_id == "") {
handleEditOrg("join")
} else {
handleEditOrg("leave")
}
}
const isOrganizationReady = () => { const isOrganizationReady = () => {
console.log("Is organization ready?")
// A simple checklist to ensure the button shows up properly // A simple checklist to ensure the button shows up properly
if (selectedOrganization.name === selectedOrganization.org) { if (selectedOrganization.name === selectedOrganization.org) {
const comment = "Change the name of your organization"
if (!publishRequirements.includes(comment)) {
setPublishRequirements([...publishRequirements, comment])
}
return false;
}
// Check if it's a suborg
if (selectedOrganization.creator_org !== "") {
const comment = "Child orgs can't become creators"
if (!publishRequirements.includes(comment)) {
setPublishRequirements([...publishRequirements, comment])
}
return false; return false;
} }
if (selectedOrganization.large_image === "" || selectedOrganization.large_image === theme.palette.defaultImage) { if (selectedOrganization.large_image === "" || selectedOrganization.large_image === theme.palette.defaultImage) {
const comment = "Add a logo for your organization"
if (!publishRequirements.includes(comment)) {
setPublishRequirements([...publishRequirements, comment])
}
return false; return false;
} }
@@ -38,40 +104,58 @@ const Branding = (props) => {
return ( return (
<div> <div>
<Typography variant="h6" style={{ marginTop: 20, marginBottom: 10 }}> <h2>
Branding Branding
</Typography> </h2>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}> <Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}>
You can customize your organization's branding by uploading a logo, changing the color scheme and a lot more. You can customize your organization's branding by uploading a logo, changing the color scheme and a lot more.
</Typography> </Typography>
<Divider style={{marginTop: 50, marginBottom: 50, }} /> <Divider style={{marginTop: 50, marginBottom: 50, }} />
<h2> <h2>
Creator Network Creator Incentive Program
</h2> </h2>
<div style={{ display: "flex", width: 700, }}> <div style={{ display: "flex", width: 900, }}>
<div> <div>
<span> <span>
<Typography variant="body1" color="textSecondary"> <Typography variant="body1" color="textSecondary">
By changing publishing settings, you agree to our <a href="/docs/terms_of_service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your organization's non-sensitive data will be turned into a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. Support: support@shuffler.io By changing publishing settings, you agree to our <a href="/docs/terms_of_service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your organization's non-sensitive data will be added as a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. None of your existing workflows, apps, or other stored data will be published. Any admin in your organization can manage the creator configuration. Becoming a creator organization is reversible.<div/>Support: <a href="mailto:support@shuffler.io"target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>support@shuffler.io</a>
</Typography> </Typography>
{selectedOrganization.creator_id == "" ?
<Typography variant="h6" color="textSecondary" style={{ marginTop: 20, marginBottom: 10, color: "grey", }}>
&nbsp;
</Typography>
:
<Typography variant="h6" color="textSecondary" style={{ marginTop: 20, marginBottom: 10, color: "grey", }}>
<a href={`/creators/${selectedOrganization.creator_id}`} target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Modify your creator organization</a>
</Typography>
}
<Button <Button
style={{ height: 40, marginTop: 10, width: 300, }} style={{ height: 40, marginTop: 10, width: 300, }}
variant="outlined" variant={selectedOrganization.creator_id == "" ? "contained" : "outlined"}
color="primary" color={selectedOrganization.creator_id == "" ? "primary" : "secondary"}
disabled={() => { disabled={!isOrganizationReady()}
return isOrganizationReady()
}}
onClick={() => { onClick={() => {
handleChangePublishing(); handleChangePublishing();
}} }}
> >
Join Creator Network {selectedOrganization.creator_id == "" ? "Join" : "Leave"} Creators
</Button> </Button>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10 }}> <Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10, color: "white", }}>
{publishingInfo} {publishingInfo}
</Typography> </Typography>
<Typography variant="body1" color="textSecondary" style={{ marginTop: 20, marginBottom: 10, color: "grey", }}>
{publishRequirements.map((item) => {
return (
<div>
Required: {item}
</div>
)
})}
</Typography>
</span> </span>
</div> </div>
</div> </div>
-1
View File
@@ -823,7 +823,6 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
color: "white", color: "white",
height: 45, height: 45,
width: 85, width: 85,
zIndex: 14999,
}} }}
value={userdata.active_org.id} value={userdata.active_org.id}
+1 -1
View File
@@ -70,7 +70,7 @@ const Priority = (props) => {
return ( return (
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", height: 95, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}> <div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: 70, maxHeight: 70, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
<div style={{flex: 2, overflow: "hidden",}}> <div style={{flex: 2, overflow: "hidden",}}>
<span style={{display: "flex", }}> <span style={{display: "flex", }}>
{priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null} {priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null}
+8 -9
View File
@@ -749,7 +749,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
image: image, image: image,
defaults: defaults, defaults: defaults,
sso_config: sso_config, sso_config: sso_config,
lead_info: lead_info, lead_info: lead_info,
}; };
const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
@@ -2371,7 +2371,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
*/} */}
{userdata.support === true ? {userdata.support === true ?
<span style={{display: "flex", top: -10, right: 50, position: "absolute"}}> <span style={{display: "flex", top: -10, right: -50, position: "absolute"}}>
{/*<a href={mailsendingButton(selectedOrganization)} target="_blank" rel="noopener noreferrer" style={{textDecoration: "none"}} disabled={selectedStatus.length !== 0}>*/} {/*<a href={mailsendingButton(selectedOrganization)} target="_blank" rel="noopener noreferrer" style={{textDecoration: "none"}} disabled={selectedStatus.length !== 0}>*/}
<Button <Button
variant="outlined" variant="outlined"
@@ -2468,8 +2468,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
setSelectedOrganization={setSelectedOrganization} setSelectedOrganization={setSelectedOrganization}
globalUrl={globalUrl} globalUrl={globalUrl}
selectedOrganization={selectedOrganization} selectedOrganization={selectedOrganization}
adminTab={adminTab} adminTab={adminTab}
handleEditOrg={handleEditOrg} handleEditOrg={handleEditOrg}
/> />
) : ( ) : (
<div <div
@@ -2517,13 +2517,13 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
<Tab <Tab
disabled={!isCloud} disabled={!isCloud}
label=<span> label=<span>
Billing (Beta) Billing
</span> </span>
/> />
<Tab <Tab
disabled={!isCloud || true} disabled={!isCloud}
label=<span> label=<span>
Branding Branding (Beta)
</span> </span>
/> />
</Tabs> </Tabs>
@@ -2798,7 +2798,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
globalUrl={globalUrl} globalUrl={globalUrl}
handleGetOrg={handleGetOrg} handleGetOrg={handleGetOrg}
selectedOrganization={selectedOrganization} selectedOrganization={selectedOrganization}
selectedOrganization={selectedOrganization}
setSelectedOrganization={setSelectedOrganization} setSelectedOrganization={setSelectedOrganization}
/> />
: null : null
@@ -2995,7 +2994,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
Add, edit, block or change passwords.{" "} Add, edit, block or change passwords.{" "}
<a <a
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
href="/docs/organizations#user_management" href="/docs/organizations#user_management"
style={{ textDecoration: "none", color: "#f85a3e" }} style={{ textDecoration: "none", color: "#f85a3e" }}
> >
+299 -29
View File
@@ -22,9 +22,11 @@ import {
Breadcrumbs, Breadcrumbs,
CircularProgress, CircularProgress,
Chip, Chip,
IconButton,
} from "@mui/material"; } from "@mui/material";
import { import {
Publish as PublishIcon,
LockOpen as LockOpenIcon, LockOpen as LockOpenIcon,
FileCopy as FileCopyIcon, FileCopy as FileCopyIcon,
Delete as DeleteIcon, Delete as DeleteIcon,
@@ -40,10 +42,11 @@ import {
ZoomOutOutlined as ZoomOutOutlinedIcon, ZoomOutOutlined as ZoomOutOutlinedIcon,
Loop as LoopIcon, Loop as LoopIcon,
AddPhotoAlternate as AddPhotoAlternateIcon, AddPhotoAlternate as AddPhotoAlternateIcon,
CallMerge as CallMergeIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { Link, useParams } from "react-router-dom"; import { useNavigate, Link, useParams } from "react-router-dom";
import YAML from "yaml"; import YAML from "yaml";
import { MuiChipsInput } from "mui-chips-input"; import { MuiChipsInput } from "mui-chips-input";
//import { useAlert //import { useAlert
@@ -360,6 +363,8 @@ const AppCreator = (defaultprops) => {
props.match.params = params props.match.params = params
var upload = ""; var upload = "";
let navigate = useNavigate();
const increaseAmount = 50; const increaseAmount = 50;
const actionNonBodyRequest = ["GET", "HEAD", "DELETE", "CONNECT"]; const actionNonBodyRequest = ["GET", "HEAD", "DELETE", "CONNECT"];
const actionBodyRequest = ["POST", "PUT", "PATCH"]; const actionBodyRequest = ["POST", "PUT", "PATCH"];
@@ -422,6 +427,109 @@ const AppCreator = (defaultprops) => {
// and make categories + labels modifyable. // and make categories + labels modifyable.
// Categories are the main categories in the App Framework // Categories are the main categories in the App Framework
const [categories, setCategories] = useState(appCategories) const [categories, setCategories] = useState(appCategories)
const redirectOpenApi = () => {
navigate(`/apps/new?id=${appValidation}`)
}
const newUpload = React.useRef(null);
const [openApiError, setOpenApiError] = React.useState("");
const [validation, setValidation] = React.useState("");
const [appValidation, setAppValidation] = React.useState("");
const [openApi, setOpenApi] = React.useState("");
const [openApiData, setOpenApiData] = React.useState("");
const [openApiModal, setOpenApiModal] = React.useState(false);
useEffect(() => {
console.log("In useEffect for openApiData: ", openApiData)
}, [openApiData]);
const uploadFile = (e) => {
console.log("In uploadFile")
const isDropzone =
e.dataTransfer === undefined ? false : e.dataTransfer.files.length > 0;
const files = isDropzone ? e.dataTransfer.files : e.target.files;
const reader = new FileReader();
try {
reader.addEventListener("load", (e) => {
const content = e.target.result;
console.log("set openapi data! ", content)
setOpenApiData(content);
setOpenApiModal(true);
});
} catch (e) {
console.log("Error in dropzone: ", e);
}
try {
reader.readAsText(files[0]);
} catch (error) {
toast("Failed to read file");
}
}
const escapeApiData = (apidata) => {
//console.log(apidata)
try {
return JSON.stringify(JSON.parse(apidata));
} catch (error) {
console.log("JSON DECODE ERROR - TRY YAML");
}
try {
const parsed = YAML.parse(YAML.stringify(apidata));
//const parsed = YAML.parse(apidata))
return YAML.stringify(parsed);
} catch (error) {
console.log("YAML DECODE ERROR - TRY SOMETHING ELSE?: " + error);
setOpenApiError("Local error: " + error.toString());
}
return "";
}
const validateOpenApi = (openApidata) => {
var newApidata = escapeApiData(openApidata);
if (newApidata === "") {
// Used to return here
newApidata = openApidata;
return;
}
//console.log(newApidata)
setValidation(true);
fetch(globalUrl + "/api/v1/validate_openapi", {
method: "POST",
headers: {
Accept: "application/json",
},
body: openApidata,
credentials: "include",
})
.then((response) => {
setValidation(false);
return response.json();
})
.then((responseJson) => {
if (responseJson.success) {
setAppValidation(responseJson.id);
} else {
if (responseJson.reason !== undefined) {
setOpenApiError(responseJson.reason);
}
toast("An error occurred in the response");
}
})
.catch((error) => {
setValidation(false);
toast(error.toString());
setOpenApiError(error.toString());
});
};
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
@@ -2084,9 +2192,9 @@ const AppCreator = (defaultprops) => {
}, },
}; };
if (queryitem.example !== undefined) { if (queryitem.example !== undefined) {
newitem.example = queryitem.example newitem.example = queryitem.example
} }
if (queryitem.description !== undefined) { if (queryitem.description !== undefined) {
newitem.description = queryitem.description; newitem.description = queryitem.description;
@@ -2936,18 +3044,6 @@ const AppCreator = (defaultprops) => {
setOauth2Scopes(chips) setOauth2Scopes(chips)
setUpdate(Math.random()) setUpdate(Math.random())
}} }}
onAdd={(chip) => {
oauth2Scopes.push(chip);
console.log(oauth2Scopes);
setOauth2Scopes(oauth2Scopes);
setUpdate(Math.random());
}}
onDelete={(chip, index) => {
oauth2Scopes.splice(index, 1);
console.log(oauth2Scopes);
setOauth2Scopes(oauth2Scopes);
setUpdate(Math.random());
}}
/> />
</div> </div>
) : null; ) : null;
@@ -4408,16 +4504,7 @@ const AppCreator = (defaultprops) => {
setNewWorkflowTags(chips) setNewWorkflowTags(chips)
setUpdate("added "+chips) setUpdate("added "+chips)
}} }}
onAdd={(chip) => {
newWorkflowTags.push(chip);
setNewWorkflowTags(newWorkflowTags);
setUpdate("added" + chip);
}}
onDelete={(chip, index) => {
newWorkflowTags.splice(index, 1);
setNewWorkflowTags(newWorkflowTags);
setUpdate("delete " + chip);
}}
/> />
</div> </div>
); );
@@ -5360,6 +5447,167 @@ const AppCreator = (defaultprops) => {
</Dialog> </Dialog>
) : null; ) : null;
const validateRemote = () => {
setValidation(true);
fetch(globalUrl + "/api/v1/get_openapi_uri", {
method: "POST",
headers: {
Accept: "application/json",
},
body: JSON.stringify(openApi),
credentials: "include",
})
.then((response) => {
setValidation(false);
if (response.status !== 200) {
return response.json();
}
return response.text();
})
.then((responseJson) => {
if (typeof responseJson !== "string" && !responseJson.success) {
console.log(responseJson.reason);
if (responseJson.reason !== undefined) {
setOpenApiError(responseJson.reason);
} else {
setOpenApiError("Undefined issue with OpenAPI validation");
}
return;
}
console.log("Validating response!");
validateOpenApi(responseJson);
})
.catch((error) => {
toast(error.toString());
setOpenApiError(error.toString());
});
}
const circularLoader = validation ? (
<CircularProgress color="primary" />
) : null;
const newApimodalView = openApiModal ?
<Dialog
open={openApiModal}
onClose={() => {
setOpenApiModal(false)
}}
PaperProps={{
style: {
backgroundColor: surfaceColor,
color: "white",
minWidth: "800px",
minHeight: "320px",
},
}}
>
<FormControl>
<DialogTitle>
<div style={{ color: "rgba(255,255,255,0.9)" }}>
Merge with another OpenAPI document. You will get to choose Actions before they are merged.
</div>
</DialogTitle>
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
Paste in the URI for the OpenAPI
<TextField
style={{ backgroundColor: inputColor }}
variant="outlined"
margin="normal"
InputProps={{
style: {
color: "white",
height: "50px",
fontSize: "1em",
},
endAdornment: (
<Button
style={{
borderRadius: "0px",
marginTop: "0px",
height: "50px",
}}
variant="contained"
disabled={openApi.length === 0 || appValidation.length > 0}
color="primary"
onClick={() => {
setOpenApiError("");
validateRemote();
}}
>
Validate
</Button>
),
}}
onChange={(e) => {
setOpenApi(e.target.value);
}}
helperText={
<span style={{ color: "white", marginBottom: "2px" }}>
Must point to a version 2 or 3 OpenAPI specification.
</span>
}
placeholder="OpenAPI URI"
fullWidth
/>
{/*
<div style={{marginTop: "15px"}}/>
Example:
<div />
https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/uber.json
*/}
<p>Or upload a YAML/JSON specification</p>
<input
hidden
type="file"
ref={newUpload}
accept="application/JSON,application/YAML,application/yaml,text/yaml,text/x-yaml,application/x-yaml,application/vnd.yaml,.yml,.yaml"
multiple={false}
onChange={uploadFile}
/>
<Button
variant="contained"
color="primary"
onClick={() => newUpload.current.click()}
>
Upload
</Button>
{errorText}
</DialogContent>
<DialogActions>
{circularLoader}
<Button
style={{ borderRadius: "0px" }}
onClick={() => {
setOpenApiModal(false);
setAppValidation("");
setOpenApiError("");
setOpenApi("");
setOpenApiData("");
}}
color="primary"
>
Cancel
</Button>
<Button
variant="contained"
style={{ borderRadius: "0px" }}
disabled={appValidation.length === 0}
onClick={() => {
redirectOpenApi();
}}
color="primary"
>
Continue
</Button>
</DialogActions>
</FormControl>
</Dialog>
: null
// Random names for type & autoComplete. Didn't research :^) // Random names for type & autoComplete. Didn't research :^)
const landingpageDataBrowser = ( const landingpageDataBrowser = (
<div style={{ paddingBottom: 100, color: "white" }}> <div style={{ paddingBottom: 100, color: "white" }}>
@@ -5391,9 +5639,30 @@ const AppCreator = (defaultprops) => {
onChange={editHeaderImage} onChange={editHeaderImage}
/> />
<Paper style={boxStyle}> <Paper style={boxStyle}>
<h2 style={{ marginBottom: "10px", color: "white" }}> <div style={{display: "flex", }}>
General information <div style={{flex: 1, }}>
</h2> <h2 style={{ marginBottom: "10px", color: "white" }}>
General information
</h2>
</div>
<div style={{flex: 1, itemAlign: "right", textAlign: "right",}}>
<Tooltip title="Merge with another API (coming soon)" placement="bottom">
<IconButton
disabled
onClick={() => {
setOpenApiModal(true)
}}
>
<CallMergeIcon
style={{}}
onClick={() => {
setOpenApiModal(true)
}}
/>
</IconButton>
</Tooltip>
</div>
</div>
<a <a
target="_blank" target="_blank"
href="https://shuffler.io/docs/app_creation#app-creator-instructions" href="https://shuffler.io/docs/app_creation#app-creator-instructions"
@@ -5706,6 +5975,7 @@ const AppCreator = (defaultprops) => {
isLoaded && isAppLoaded ? ( isLoaded && isAppLoaded ? (
<div> <div>
<div style={bodyDivStyle}>{landingpageDataBrowser}</div> <div style={bodyDivStyle}>{landingpageDataBrowser}</div>
{newApimodalView}
</div> </div>
) : ( ) : (
<div></div> <div></div>
+2 -1
View File
@@ -1354,7 +1354,7 @@ const Apps = (props) => {
border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)",
cursor: hover ? "pointer" : "default", cursor: hover ? "pointer" : "default",
textAlign: "center", textAlign: "center",
height: 150, height: 125,
}} }}
> >
{icon} {icon}
@@ -2556,6 +2556,7 @@ const Apps = (props) => {
const circularLoader = validation ? ( const circularLoader = validation ? (
<CircularProgress color="primary" /> <CircularProgress color="primary" />
) : null; ) : null;
const appsModalLoad = loadAppsModalOpen ? ( const appsModalLoad = loadAppsModalOpen ? (
<Dialog <Dialog
open={loadAppsModalOpen} open={loadAppsModalOpen}
+21 -21
View File
@@ -730,28 +730,28 @@ const Settings = (props) => {
</Button> </Button>
<h3>{passwordFormMessage}</h3> <h3>{passwordFormMessage}</h3>
<Divider style={{ marginTop: "40px" }} /> <Divider style={{ marginTop: "40px" }} />
<h2>Platform Earnings</h2> <h2>Creator Incentive Program</h2>
<div style={{ display: runFlex ? "flex" : "", width: "100%" }}> <div style={{ display: runFlex ? "flex" : "", width: "100%" }}>
<div> <div>
{isCloud ? {isCloud ?
<span> <span>
<Typography variant="body1" color="textSecondary"> <Typography variant="body1" color="textSecondary">
By connecting your Github account, you agree to our <a href="/docs/terms_of_service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your non-sensitive data will be turned into a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. This enables you to earn a passive income from Shuffle. This IS reversible. Support: support@shuffler.io By <a href="/creators" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>joining the Creator Incentive Program</a> and connecting your Github account, you agree to our <a href="/docs/terms_of_service" target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}}>Terms of Service</a>, and acknowledge that your non-sensitive data will be turned into a <a target="_blank" style={{ textDecoration: "none", color: "#f86a3e"}} href="https://shuffler.io/creators">creator account</a>. This enables you to earn a passive income from Shuffle. This IS reversible. Support: support@shuffler.io
</Typography> </Typography>
<Button <Button
style={{ height: 40, marginTop: 10 }} style={{ height: 40, marginTop: 10 }}
variant="outlined" variant="outlined"
color="primary" color="primary"
fullWidth={true} fullWidth={true}
onClick={() => { onClick={() => {
handleGithubConnection(); handleGithubConnection();
}} }}
> >
Connect to Github Connect to Github
</Button> </Button>
</span> </span>
: null} : null}
</div> </div>
<div style={{ flex: 1, display: "flex" }}> <div style={{ flex: 1, display: "flex" }}>
<div> <div>
{userdata.eth_info !== undefined && {userdata.eth_info !== undefined &&
+17 -11
View File
@@ -638,7 +638,7 @@ const Workflows = (props) => {
window.location.host === "shuffler.io"; window.location.host === "shuffler.io";
const findWorkflow = (filters) => { const findWorkflow = (filters) => {
console.log("Using filters: ", filters) console.log("Using filters: ", filters)
if (filters.length === 0) { if (filters.length === 0) {
setFilteredWorkflows(workflows); setFilteredWorkflows(workflows);
handleKeysetting(allUsecases, workflows) handleKeysetting(allUsecases, workflows)
@@ -656,6 +656,12 @@ const Workflows = (props) => {
); );
} }
if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.length > 0) {
// Make them all lowercase
curWorkflow.tags = curWorkflow.tags.map((tag) => tag.toLowerCase())
}
if (found.every((v) => v !== true)) { if (found.every((v) => v !== true)) {
found = filters.map((filter) => { found = filters.map((filter) => {
if (filter === undefined || filter === null) { if (filter === undefined || filter === null) {
@@ -666,7 +672,7 @@ const Workflows = (props) => {
if (curWorkflow.name.toLowerCase().includes(filter.toLowerCase())) { if (curWorkflow.name.toLowerCase().includes(filter.toLowerCase())) {
return true; return true;
} else if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.includes(filter)) { } else if (curWorkflow.tags !== undefined && curWorkflow.tags !== null && curWorkflow.tags.includes(filter.toLowerCase())) {
return true; return true;
} else if (curWorkflow.owner === filter) { } else if (curWorkflow.owner === filter) {
return true; return true;
@@ -674,17 +680,17 @@ const Workflows = (props) => {
return true; return true;
} else if (curWorkflow.usecase_ids !== undefined && curWorkflow.usecase_ids !== null && curWorkflow.usecase_ids.length > 0) { } else if (curWorkflow.usecase_ids !== undefined && curWorkflow.usecase_ids !== null && curWorkflow.usecase_ids.length > 0) {
// Check if the usecase is the right category // Check if the usecase is the right category
for (var key in usecases) { for (var key in usecases) {
if (usecases[key].name.toLowerCase() !== newfilter) { if (usecases[key].name.toLowerCase() !== newfilter) {
continue continue
} }
for (var subkey in usecases[key].list) { for (var subkey in usecases[key].list) {
if (curWorkflow.usecase_ids.includes(usecases[key].list[subkey].name)) { if (curWorkflow.usecase_ids.includes(usecases[key].list[subkey].name)) {
return true return true
}
}
} }
}
}
} else if ( } else if (
curWorkflow.actions !== null && curWorkflow.actions !== null &&
curWorkflow.actions !== undefined curWorkflow.actions !== undefined
+2 -2
View File
@@ -224,7 +224,7 @@ func deployServiceWorkers(image string) {
} }
if err != nil { if err != nil {
log.Printf("[ERROR] Failed to convert the default MTU to int: %s. Using 1500 instead", err) log.Printf("[DEBUG] Failed to convert the default MTU to int: %s. Using 1500 instead", err)
mtu = 1500 mtu = 1500
} }
@@ -1601,6 +1601,6 @@ func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest) error {
_ = body _ = body
log.Printf("[DEBUG] Ran worker from request with execution ID: %s. Worker URL: %s. DEBUGGING: docker service logs shuffle-workers 2&>1 | grep %s", workflowExecution.ExecutionId, streamUrl, workflowExecution.ExecutionId) log.Printf("[DEBUG] Ran worker from request with execution ID: %s. Worker URL: %s. DEBUGGING: docker service logs shuffle-workers 2>&1 | grep %s", workflowExecution.ExecutionId, streamUrl, workflowExecution.ExecutionId)
return nil return nil
} }