Merge branch 'nightly' into automation
@@ -984,11 +984,4 @@ func activateWorkflowAppDocker(resp http.ResponseWriter, request *http.Request)
|
||||
|
||||
log.Printf("[INFO] User %s (%s) is activating %s. Public: %t, Shared: %t", user.Username, user.Id, app.Name, app.Public, app.Sharing)
|
||||
buildSwaggerApp(resp, []byte(openApiApp.Body), user, true)
|
||||
|
||||
//app.Active = true
|
||||
//app.Generated = true
|
||||
//app, err := shuffle.SetApp(ctx, app)
|
||||
|
||||
//resp.WriteHeader(200)
|
||||
//resp.Write([]byte(`{"success": true}`))
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.8.3
|
||||
github.com/shuffle/shuffle-shared v0.8.12
|
||||
golang.org/x/crypto v0.32.0
|
||||
google.golang.org/api v0.176.1
|
||||
google.golang.org/grpc v1.68.1
|
||||
|
||||
@@ -350,6 +350,14 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdR
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shuffle/shuffle-shared v0.8.4 h1:R/A62IzHJXnhVNG1PqFXN1YtRgdiiSIL5q4YJEef5P8=
|
||||
github.com/shuffle/shuffle-shared v0.8.4/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ=
|
||||
github.com/shuffle/shuffle-shared v0.8.5 h1:c98AMOzIrDXmgQrKyf1HJ9wZwAwB02LPRvf9+yy05OQ=
|
||||
github.com/shuffle/shuffle-shared v0.8.5/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ=
|
||||
github.com/shuffle/shuffle-shared v0.8.6 h1:chnGJRKsO1gR5Vt/8hp5SQg1yP8/DmiK8sRhWVaTgEg=
|
||||
github.com/shuffle/shuffle-shared v0.8.6/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ=
|
||||
github.com/shuffle/shuffle-shared v0.8.12 h1:fg6jGAuevOGLgR7kpoZDJh//tzFjrEqj/bpxRFzxUTw=
|
||||
github.com/shuffle/shuffle-shared v0.8.12/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
|
||||
@@ -3295,6 +3295,8 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s
|
||||
api.Contributors = append(api.Contributors, user.Id)
|
||||
}
|
||||
|
||||
shuffle.SetAppRevision(ctx, api)
|
||||
|
||||
log.Printf("[INFO] API LENGTH FOR %s: %d, ID: %s", api.Name, len(parsed.Body), newmd5)
|
||||
// FIXME: Might cause versioning issues if we re-use the same!!
|
||||
// FIXME: Need a way to track different versions of the same app properly.
|
||||
@@ -3365,11 +3367,27 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User, s
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
log.Printf("[DEBUG] Successfully built app %s (%s)", api.Name, api.ID)
|
||||
if len(user.Id) > 0 {
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "id": "%s"}`, api.ID)))
|
||||
}
|
||||
|
||||
org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed getting org during image build (%s): %s", user.ActiveOrg.Id, err)
|
||||
} else {
|
||||
imagenames := []string{
|
||||
fmt.Sprintf("%s_%s", api.Name, api.AppVersion),
|
||||
fmt.Sprintf("%s_%s", api.Name, api.ID),
|
||||
}
|
||||
|
||||
err = shuffle.DistributeAppToEnvironments(ctx, *org, imagenames)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed distributing app to environments: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Creates an app from the app builder
|
||||
@@ -4359,8 +4377,8 @@ func runInitEs(ctx context.Context) {
|
||||
}
|
||||
|
||||
if os.Getenv("SHUFFLE_HEALTHCHECK_DISABLED") != "true" {
|
||||
healthcheckInterval := 30
|
||||
log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval)
|
||||
healthcheckInterval := 60
|
||||
log.Printf("[INFO] Starting healthcheck job every %d minute. Stats available on /api/v1/health/stats, and dashboard on /health. Disable with SHUFFLE_HEALTHCHECK_DISABLED=true", healthcheckInterval)
|
||||
job := func() {
|
||||
// Prepare a fake http.responsewriter
|
||||
resp := httptest.NewRecorder()
|
||||
|
||||
@@ -1032,10 +1032,14 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
|
||||
log.Printf("[INFO] Should have deleted workflow %s (%s)", workflow.Name, fileId)
|
||||
|
||||
cacheKey := fmt.Sprintf("%s_workflows", user.Id)
|
||||
shuffle.DeleteCache(ctx, cacheKey)
|
||||
shuffle.DeleteCache(ctx, fmt.Sprintf("%s_workflows", user.Id))
|
||||
shuffle.DeleteCache(ctx, fmt.Sprintf("%s_workflows", user.ActiveOrg.Id))
|
||||
shuffle.DeleteCache(ctx, fmt.Sprintf("%s_%s", user.Username, fileId))
|
||||
log.Printf("[DEBUG] Cleared workflow cache for %s (%s)", user.Username, user.Id)
|
||||
shuffle.DeleteCache(ctx, fmt.Sprintf("workflow_%s_childworkflows", workflow.ID))
|
||||
if len(workflow.ParentWorkflowId) > 0 {
|
||||
shuffle.DeleteCache(ctx, fmt.Sprintf("workflow_%s_childworkflows", workflow.ParentWorkflowId))
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(`{"success": true}`))
|
||||
@@ -1094,10 +1098,12 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
workflow.Errors = []string{}
|
||||
}
|
||||
|
||||
/*
|
||||
if !workflow.IsValid {
|
||||
log.Printf("[ERROR] Stopped execution as workflow %s is not valid.", workflow.ID)
|
||||
return shuffle.WorkflowExecution{}, fmt.Sprintf(`workflow %s is invalid`, workflow.ID), errors.New("Failed getting workflow")
|
||||
}
|
||||
*/
|
||||
|
||||
maxExecutionDepth := 10
|
||||
if os.Getenv("SHUFFLE_MAX_EXECUTION_DEPTH") != "" {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
frontend:
|
||||
image: ghcr.io/shuffle/shuffle-frontend:nightly
|
||||
image: ghcr.io/shuffle/shuffle-frontend:latest
|
||||
container_name: shuffle-frontend
|
||||
hostname: shuffle-frontend
|
||||
ports:
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
depends_on:
|
||||
- backend
|
||||
backend:
|
||||
image: ghcr.io/shuffle/shuffle-backend:nightly
|
||||
image: ghcr.io/shuffle/shuffle-backend:latest
|
||||
container_name: shuffle-backend
|
||||
hostname: ${BACKEND_HOSTNAME}
|
||||
# Here for debugging:
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
- SHUFFLE_FILE_LOCATION=/shuffle-files
|
||||
restart: unless-stopped
|
||||
orborus:
|
||||
image: ghcr.io/shuffle/shuffle-orborus:nightly
|
||||
image: ghcr.io/shuffle/shuffle-orborus:latest
|
||||
container_name: shuffle-orborus
|
||||
hostname: shuffle-orborus
|
||||
networks:
|
||||
@@ -55,13 +55,13 @@ services:
|
||||
- SHUFFLE_STATS_DISABLED=true
|
||||
- SHUFFLE_LOGS_DISABLED=true
|
||||
- SHUFFLE_SWARM_CONFIG=run
|
||||
- SHUFFLE_WORKER_IMAGE=ghcr.io/shuffle/shuffle-worker:nightly
|
||||
- SHUFFLE_WORKER_IMAGE=ghcr.io/shuffle/shuffle-worker:latest
|
||||
env_file: .env
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- seccomp:unconfined
|
||||
opensearch:
|
||||
image: opensearchproject/opensearch:2.14.0
|
||||
image: opensearchproject/opensearch:2.19.1
|
||||
hostname: shuffle-opensearch
|
||||
container_name: shuffle-opensearch
|
||||
environment:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg width="45" height="56" viewBox="0 0 45 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.866 31.366L22.2583 37.366L13.3454 52.8036L11.2901 46.9667C10.9625 46.0363 10.0053 45.4837 9.03569 45.6651L2.95314 46.8036L11.866 31.366Z" stroke="#2BC07E" stroke-width="2"/>
|
||||
<path d="M33.759 31.366L23.3667 37.366L32.2796 52.8036L34.3349 46.9667C34.6625 46.0363 35.6197 45.4837 36.5893 45.6651L42.6719 46.8036L33.759 31.366Z" stroke="#2BC07E" stroke-width="2"/>
|
||||
<path d="M18.6381 6.27038C20.6383 3.8382 24.3617 3.8382 26.3619 6.27038L26.7961 6.79841C28.1024 8.38685 30.0411 9.32049 32.0974 9.35141L32.781 9.36168C35.9296 9.40902 38.2511 12.3201 37.5966 15.4003L37.4546 16.069C37.0271 18.0807 37.5059 20.1786 38.7639 21.8056L39.182 22.3464C41.1082 24.8376 40.2796 28.4677 37.4634 29.8765L36.852 30.1823C35.0127 31.1024 33.671 32.7847 33.1833 34.7826L33.0212 35.4468C32.2744 38.5059 28.9197 40.1215 26.0624 38.798L25.442 38.5106C23.5759 37.6463 21.4241 37.6463 19.558 38.5106L18.9376 38.798C16.0803 40.1215 12.7256 38.5059 11.9788 35.4468L11.8167 34.7826C11.329 32.7847 9.98734 31.1024 8.14805 30.1823L7.53663 29.8765C4.72036 28.4677 3.89182 24.8376 5.81795 22.3464L6.23611 21.8056C7.49405 20.1786 7.97288 18.0807 7.54544 16.069L7.40335 15.4003C6.74887 12.3201 9.07037 9.40902 12.219 9.36168L12.9026 9.35141C14.9589 9.32049 16.8976 8.38685 18.2039 6.79841L17.4316 6.16323L18.2039 6.79841L18.6381 6.27038Z" fill="#2F2F2F" stroke="#2BC07E" stroke-width="2"/>
|
||||
<path d="M30.5 18.5L21 28L16 23" stroke="#2BC07E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="54" height="52" viewBox="0 0 54 52" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M37.9999 31.4723C38.007 34.1853 37.3731 36.8617 36.1499 39.2834C34.6996 42.1853 32.47 44.6261 29.7108 46.3324C26.9517 48.0387 23.7719 48.9432 20.5277 48.9444C17.9537 48.9512 15.4126 48.3809 13.0908 47.2778C12.8499 47.1634 12.5744 47.1419 12.3214 47.2262L2.89737 50.3675C2.11561 50.6281 1.37187 49.8844 1.63246 49.1026L4.77381 39.6786C4.85812 39.4256 4.83665 39.1501 4.72223 38.9092C3.6191 36.5874 3.04884 34.0463 3.05555 31.4723C3.05681 28.2281 3.96125 25.0483 5.66758 22.2892C7.37391 19.53 9.81473 17.3004 12.7166 15.8501C15.1383 14.6269 17.8147 13.993 20.5277 14.0001H21.5555C25.84 14.2364 29.8868 16.0448 32.921 19.079C35.9552 22.1132 37.7636 26.16 37.9999 30.4445V31.4723Z" stroke="#4FB1E8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M39 32L38.0926 33.7823C38.6627 34.0726 39.3373 34.0726 39.9074 33.7823L39 32ZM39 32C39.9074 33.7823 39.9079 33.782 39.9085 33.7817L39.91 33.781L39.9136 33.7791L39.9239 33.7739L39.9562 33.7571C39.9829 33.7432 40.0199 33.7237 40.0664 33.6989C40.1593 33.6492 40.2906 33.5778 40.4547 33.4853C40.7826 33.3004 41.2432 33.0306 41.7925 32.6811C42.8869 31.9846 44.3541 30.9586 45.8304 29.6433C48.7043 27.083 52 23.078 52 18V8.2C52 7.37067 51.4882 6.62739 50.7134 6.33156L39.7134 2.13156C39.254 1.95615 38.746 1.95615 38.2866 2.13156L27.2866 6.33156C26.5118 6.62739 26 7.37067 26 8.2V18C26 23.078 29.2957 27.083 32.1696 29.6433C33.6459 30.9586 35.1131 31.9846 36.2075 32.6811C36.7568 33.0306 37.2174 33.3004 37.5453 33.4853C37.7094 33.5778 37.8407 33.6492 37.9336 33.6989C37.9801 33.7237 38.0171 33.7432 38.0438 33.7571L38.0761 33.7739L38.0864 33.7791L38.09 33.781L38.0915 33.7817C38.0921 33.782 38.0926 33.7823 39 32Z" fill="#2F2F2F" stroke="#2F2F2F" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M40.1819 30.1501C39.6936 30.4608 39.2864 30.6997 39 30.8615C38.7136 30.6997 38.3064 30.4608 37.8181 30.1501C36.8185 29.5139 35.4896 28.5832 34.1652 27.4033C31.4771 25.0085 29 21.761 29 18V8.8886L39 5.07041L49 8.8886V18C49 21.761 46.5229 25.0085 43.8348 27.4033C42.5104 28.5832 41.1815 29.5139 40.1819 30.1501Z" stroke="#4FB1E8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.2326 41.4787H19.2326L19.2326 41.47C19.2313 40.1671 19.6607 38.9 20.4546 37.865C21.2485 36.8301 22.3625 36.0851 23.6242 35.7458C24.8858 35.4065 26.2244 35.4919 27.4325 35.9887C28.6405 36.4855 29.6503 37.3659 30.3056 38.4931L30.5302 38.8796L30.9393 38.6995C33.4578 37.591 35.237 36.1436 36.3734 34.1074C37.5026 32.084 37.9652 29.5351 37.9652 26.2827V19.274C37.9652 14.5199 36.6216 10.455 34.1879 7.56945C31.7474 4.6759 28.2394 3.00436 24.0012 3.00436C19.7629 3.00436 16.2549 4.67653 13.8145 7.57034C11.3808 10.4561 10.0371 14.521 10.0371 19.274V27.4501C10.0367 27.7819 9.90438 28.1002 9.66889 28.3351C9.43337 28.5701 9.11382 28.7025 8.78025 28.703H6.43988C4.86506 28.7013 3.35543 28.0765 2.242 26.966C1.1287 25.8556 0.502381 24.3503 0.5 22.7803V20.4447C0.501784 18.874 1.12781 17.3678 2.24116 16.2566C3.35447 15.1454 4.86421 14.5199 6.43943 14.5174C6.43964 14.5174 6.43985 14.5174 6.44006 14.5174L7.55861 14.5174H7.96354L8.04769 14.1213C9.77814 5.97693 15.6661 0.5 24.0012 0.5C32.3361 0.5 38.2218 5.97684 39.9523 14.1213L40.0365 14.5174H40.4414L41.5599 14.5174C41.5602 14.5174 41.5604 14.5174 41.5606 14.5174C43.1354 14.5199 44.6448 15.1451 45.7581 16.2558C46.8714 17.3667 47.4977 18.8724 47.5 20.4428V22.7778C47.4983 24.3483 46.8722 25.8543 45.7588 26.9652C44.6453 28.0762 43.1353 28.7013 41.5601 28.703H40.8371H40.3532L40.3374 29.1866C40.2503 31.8522 39.354 34.4287 37.7668 36.5749C36.1796 38.7212 33.9764 40.3361 31.4486 41.2057L31.1171 41.3197L31.1114 41.6704C31.0859 43.2413 30.436 44.7381 29.3042 45.8313C28.1724 46.9246 26.6514 47.5247 25.0758 47.4992C23.5002 47.4738 21.9995 46.8249 20.9038 45.6957C19.8081 44.5666 19.2071 43.0496 19.2326 41.4787ZM21.745 41.4701V41.4705C21.745 42.1473 21.9461 42.8087 22.3228 43.3712C22.6995 43.9337 23.2348 44.3719 23.8609 44.6306C24.4869 44.8894 25.1758 44.9571 25.8403 44.8252C26.5049 44.6933 27.1154 44.3677 27.5948 43.8895C28.0741 43.4112 28.4007 42.8017 28.533 42.138C28.6654 41.4743 28.5974 40.7864 28.3379 40.1613C28.0784 39.5362 27.639 39.0021 27.0754 38.6264C26.5119 38.2507 25.8495 38.0502 25.172 38.0502H25.1716C24.2634 38.051 23.3923 38.4112 22.7498 39.0523C22.1072 39.6934 21.7457 40.563 21.745 41.4701ZM40.4776 25.7009V26.2009H40.9776H41.5607C42.4693 26.2009 43.341 25.841 43.9842 25.1999C44.6273 24.5587 44.9892 23.6887 44.99 22.781V22.7806V20.4444C44.99 19.5368 44.6288 18.6665 43.9861 18.0248C43.3435 17.3831 42.4721 17.0225 41.5634 17.0218H41.563H40.9776H40.4776V17.5218V25.7009ZM3.01237 22.7806L3.01237 22.7815C3.01382 23.6881 3.37566 24.557 4.01816 25.1975L4.37117 24.8434L4.01816 25.1975C4.66062 25.838 5.53122 26.1979 6.43894 26.1986H6.43934H7.02473H7.52473V25.6986V17.5218V17.0218H7.02473H6.43934H6.43894C5.53027 17.0225 4.65884 17.3831 4.01622 18.0248C3.37357 18.6665 3.01237 19.5368 3.01237 20.4444L3.01237 22.7806Z" fill="#FF8544" stroke="#2F2F2F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="65" height="45" viewBox="0 0 65 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M28.5445 38.9971V36.0579C28.5445 34.4989 27.9252 33.0037 26.8228 31.9013C25.7204 30.7989 24.2253 30.1796 22.6662 30.1796H10.9096C9.35055 30.1796 7.85537 30.7989 6.75297 31.9013C5.65057 33.0037 5.03125 34.4989 5.03125 36.0579V38.9971" stroke="#806BFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M16.7885 25.8205C20.035 25.8205 22.6668 23.1887 22.6668 19.9422C22.6668 16.6957 20.035 14.0638 16.7885 14.0638C13.542 14.0638 10.9102 16.6957 10.9102 19.9422C10.9102 23.1887 13.542 25.8205 16.7885 25.8205Z" stroke="#806BFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M1 43.9362H32.575" stroke="#806BFF" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M16.5742 8.16418V5C16.5742 2.79086 18.3651 1 20.5742 1H59.5742C61.7834 1 63.5742 2.79086 63.5742 5V29C63.5742 31.2091 61.7834 33 59.5742 33H33.3907" stroke="#806BFF" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="66" height="28" viewBox="0 0 66 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.25391 27.1562C5.13411 27.0521 3.82552 27 2.32812 27H0.785156L9.28125 3.19141C9.65885 2.1237 9.84766 1.50521 9.84766 1.33594C9.84766 1.15365 9.84115 1.04297 9.82812 1.00391L9.84766 0.964844C10.694 1.00391 11.5924 1.02344 12.543 1.02344C13.3242 1.02344 14.2617 1.01042 15.3555 0.984375L15.375 1.02344C15.349 1.07552 15.3359 1.14714 15.3359 1.23828C15.3359 1.48568 15.5312 2.14323 15.9219 3.21094L24.5938 27.0586C24.3333 27.0456 24.0273 27.0391 23.6758 27.0391C23.6758 27.0391 23.3372 27.026 22.6602 27C22.3346 27 22.0417 27 21.7812 27H18.7539L16.918 21.375H8.08984L6.25391 27.1562ZM15.3945 16.6875L12.4453 7.60547L9.57422 16.6875H15.3945ZM27.3867 0.886719C29.1966 0.652344 31.0521 0.535156 32.9531 0.535156C36.4948 0.535156 39.1836 1.36849 41.0195 3.03516C42.8685 4.6888 43.793 7.16927 43.793 10.4766C43.793 13.4323 42.7839 15.7695 40.7656 17.4883C38.8776 19.1029 36.4492 19.9102 33.4805 19.9102C33.1419 19.9102 32.7708 19.8906 32.3672 19.8516V27H27.3867V0.886719ZM33.5391 5.24219C33.1484 5.24219 32.7578 5.25521 32.3672 5.28125V15.5547C32.7839 15.6068 33.2917 15.6328 33.8906 15.6328C34.4896 15.6328 35.1146 15.5091 35.7656 15.2617C36.4167 15.0013 36.9635 14.6432 37.4062 14.1875C38.3307 13.237 38.793 11.987 38.793 10.4375C38.793 6.97396 37.0417 5.24219 33.5391 5.24219ZM48.4023 0.945312C49.7044 0.997396 50.4922 1.02344 50.7656 1.02344H52.1719C52.862 1.02344 53.4154 0.971354 53.832 0.867188L53.8125 27H48.4023V0.945312ZM59.9258 27C60.043 26.2057 60.1016 25.5026 60.1016 24.8906C60.1016 24.2786 60.0951 23.862 60.082 23.6406C60.082 23.4062 60.0755 23.1784 60.0625 22.957C60.0495 22.7357 60.0365 22.5273 60.0234 22.332L59.9648 21.8438H65.6289C65.5898 22.3385 65.5703 22.7422 65.5703 23.0547L65.5312 23.875C65.5312 24.1224 65.5312 24.5911 65.5312 25.2812C65.5312 25.9583 65.5638 26.5312 65.6289 27H59.9258Z" fill="#2BC07E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="22" height="16" viewBox="0 0 22 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.9263 14.5851V13.0757C8.9263 12.275 9.24436 11.5071 9.81052 10.941C10.3767 10.3748 11.1445 10.0568 11.9452 10.0568H17.983C18.7837 10.0568 19.5516 10.3748 20.1177 10.941C20.6839 11.5071 21.002 12.275 21.002 13.0757V14.5851" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14.9635 7.03783C13.2962 7.03783 11.9446 5.68621 11.9446 4.01891C11.9446 2.35161 13.2962 1 14.9635 1C16.6308 1 17.9824 2.35161 17.9824 4.01891C17.9824 5.68621 16.6308 7.03783 14.9635 7.03783Z" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M0.99923 12.887V11.6292C0.99923 10.9619 1.26428 10.3221 1.73608 9.85025C2.20788 9.37846 2.84777 9.1134 3.51499 9.1134L7.98047 9.1134" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.03111 6.59763C4.6417 6.59763 3.51535 5.47128 3.51535 4.08186C3.51535 2.69245 4.6417 1.5661 6.03111 1.5661C7.42053 1.5661 8.54688 2.69245 8.54688 4.08186C8.54688 5.47128 7.42053 6.59763 6.03111 6.59763Z" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="22" height="16" viewBox="0 0 22 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.9263 14.5851V13.0757C8.9263 12.275 9.24436 11.5071 9.81052 10.941C10.3767 10.3748 11.1445 10.0568 11.9452 10.0568H17.983C18.7837 10.0568 19.5516 10.3748 20.1177 10.941C20.6839 11.5071 21.002 12.275 21.002 13.0757V14.5851" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14.9635 7.03783C13.2962 7.03783 11.9446 5.68621 11.9446 4.01891C11.9446 2.35161 13.2962 1 14.9635 1C16.6308 1 17.9824 2.35161 17.9824 4.01891C17.9824 5.68621 16.6308 7.03783 14.9635 7.03783Z" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M0.99923 12.887V11.6292C0.99923 10.9619 1.26428 10.3221 1.73608 9.85025C2.20788 9.37846 2.84777 9.1134 3.51499 9.1134L7.98047 9.1134" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.03111 6.59763C4.6417 6.59763 3.51535 5.47128 3.51535 4.08186C3.51535 2.69245 4.6417 1.5661 6.03111 1.5661C7.42053 1.5661 8.54688 2.69245 8.54688 4.08186C8.54688 5.47128 7.42053 6.59763 6.03111 6.59763Z" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.3214 5H11.4351H12.5488" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="0.501953" y="0.5" width="15" height="19" rx="1.5" stroke="#9E9E9E"/>
|
||||
<path d="M3.63903 12H12.5488" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.63903 15.6666H12.5488" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.3214 8.33331H11.4351H12.5488" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 590 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.3214 5H11.4351H12.5488" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="0.501953" y="0.5" width="15" height="19" rx="1.5" stroke="#FF8544"/>
|
||||
<path d="M3.63903 12H12.5488" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.63903 15.6666H12.5488" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.3214 8.33331H11.4351H12.5488" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 590 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.502 8.50003L17.502 8.50133C17.5052 9.74245 17.2152 10.9668 16.6557 12.0746L16.6547 12.0765C15.9906 13.4052 14.9698 14.5228 13.7064 15.3041C12.4431 16.0853 10.9872 16.4995 9.50176 16.5L9.50065 16.5C8.25953 16.5033 7.0352 16.2133 5.92738 15.6537C5.80838 15.5936 5.67031 15.5835 5.54384 15.6257L0.792523 17.2095L2.37629 12.4581C2.41845 12.3317 2.40836 12.1936 2.34825 12.0746C1.78869 10.9668 1.49872 9.74245 1.50195 8.50133L1.50195 8.50022C1.50253 7.01482 1.91665 5.55891 2.69792 4.29556C3.4792 3.03222 4.59678 2.01134 5.92548 1.34728L5.92548 1.34728L5.92738 1.34633C7.0352 0.786764 8.25953 0.496791 9.50065 0.500028H9.50195H9.98796C11.9444 0.611298 13.7917 1.43858 15.1775 2.82444C16.5634 4.21031 17.3907 6.05759 17.502 8.01402L17.502 8.50003Z" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 925 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.502 8.50003L17.502 8.50133C17.5052 9.74245 17.2152 10.9668 16.6557 12.0746L16.6547 12.0765C15.9906 13.4052 14.9698 14.5228 13.7064 15.3041C12.4431 16.0853 10.9872 16.4995 9.50176 16.5L9.50065 16.5C8.25953 16.5033 7.0352 16.2133 5.92738 15.6537C5.80838 15.5936 5.67031 15.5835 5.54384 15.6257L0.792523 17.2095L2.37629 12.4581C2.41845 12.3317 2.40836 12.1936 2.34825 12.0746C1.78869 10.9668 1.49872 9.74245 1.50195 8.50133L1.50195 8.50022C1.50253 7.01482 1.91665 5.55891 2.69792 4.29556C3.4792 3.03222 4.59678 2.01134 5.92548 1.34728L5.92548 1.34728L5.92738 1.34633C7.0352 0.786764 8.25953 0.496791 9.50065 0.500028H9.50195H9.98796C11.9444 0.611298 13.7917 1.43858 15.1775 2.82444C16.5634 4.21031 17.3907 6.05759 17.502 8.01402L17.502 8.50003Z" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 925 B |
|
After Width: | Height: | Size: 62 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="22" viewBox="0 0 18 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.002 1H3.00195C2.47152 1 1.96281 1.21071 1.58774 1.58579C1.21267 1.96086 1.00195 2.46957 1.00195 3V19C1.00195 19.5304 1.21267 20.0391 1.58774 20.4142C1.96281 20.7893 2.47152 21 3.00195 21H15.002C15.5324 21 16.0411 20.7893 16.4162 20.4142C16.7912 20.0391 17.002 19.5304 17.002 19V8L10.002 1Z" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.002 1V8H17.002" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 568 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="22" viewBox="0 0 18 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.002 1H3.00195C2.47152 1 1.96281 1.21071 1.58774 1.58579C1.21267 1.96086 1.00195 2.46957 1.00195 3V19C1.00195 19.5304 1.21267 20.0391 1.58774 20.4142C1.96281 20.7893 2.47152 21 3.00195 21H15.002C15.5324 21 16.0411 20.7893 16.4162 20.4142C16.7912 20.0391 17.002 19.5304 17.002 19V8L10.002 1Z" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.002 1V8H17.002" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 568 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.002 19C14.9725 19 19.002 14.9706 19.002 10C19.002 5.02944 14.9725 1 10.002 1C5.03139 1 1.00195 5.02944 1.00195 10C1.00195 14.9706 5.03139 19 10.002 19Z" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.38281 7.3C7.59441 6.6985 8.01205 6.1913 8.56177 5.86822C9.11149 5.54514 9.75782 5.42704 10.3863 5.53484C11.0147 5.64264 11.5847 5.96937 11.9954 6.45718C12.406 6.94498 12.6308 7.56237 12.6298 8.2C12.6298 10 9.92981 10.9 9.92981 10.9" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.002 14.5H10.011" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.002 19C14.9725 19 19.002 14.9706 19.002 10C19.002 5.02944 14.9725 1 10.002 1C5.03139 1 1.00195 5.02944 1.00195 10C1.00195 14.9706 5.03139 19 10.002 19Z" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.38281 7.3C7.59441 6.6985 8.01205 6.1913 8.56177 5.86822C9.11149 5.54514 9.75782 5.42704 10.3863 5.53484C11.0147 5.64264 11.5847 5.96937 11.9954 6.45718C12.406 6.94498 12.6308 7.56237 12.6298 8.2C12.6298 10 9.92981 10.9 9.92981 10.9" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.002 14.5H10.011" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
@@ -0,0 +1,11 @@
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="56" height="56" rx="4" fill="#2F2F2F"/>
|
||||
<g clip-path="url(#clip0_3970_2549)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M28 12C19.16 12 12 19.16 12 28C12 35.08 16.58 41.06 22.94 43.18C23.74 43.32 24.04 42.84 24.04 42.42C24.04 42.04 24.02 40.78 24.02 39.44C20 40.18 18.96 38.46 18.64 37.56C18.46 37.1 17.68 35.68 17 35.3C16.44 35 15.64 34.26 16.98 34.24C18.24 34.22 19.14 35.4 19.44 35.88C20.88 38.3 23.18 37.62 24.1 37.2C24.24 36.16 24.66 35.46 25.12 35.06C21.56 34.66 17.84 33.28 17.84 27.16C17.84 25.42 18.46 23.98 19.48 22.86C19.32 22.46 18.76 20.82 19.64 18.62C19.64 18.62 20.98 18.2 24.04 20.26C25.32 19.9 26.68 19.72 28.04 19.72C29.4 19.72 30.76 19.9 32.04 20.26C35.1 18.18 36.44 18.62 36.44 18.62C37.32 20.82 36.76 22.46 36.6 22.86C37.62 23.98 38.24 25.4 38.24 27.16C38.24 33.3 34.5 34.66 30.94 35.06C31.52 35.56 32.02 36.52 32.02 38.02C32.02 40.16 32 41.88 32 42.42C32 42.84 32.3 43.34 33.1 43.18C39.42 41.06 44 35.06 44 28C44 19.16 36.84 12 28 12Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_3970_2549">
|
||||
<rect width="32" height="32" fill="white" transform="translate(12 12)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 188 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.22417 1H1.00195V7.22222H7.22417V1Z" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17.0015 1H10.7793V7.22222H17.0015V1Z" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17.0015 10.7778H10.7793V17H17.0015V10.7778Z" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.22417 10.7778H1.00195V17H7.22417V10.7778Z" stroke="#9E9E9E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 573 B |
@@ -0,0 +1,6 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.22417 1H1.00195V7.22222H7.22417V1Z" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17.0015 1H10.7793V7.22222H17.0015V1Z" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M17.0015 10.7778H10.7793V17H17.0015V10.7778Z" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.22417 10.7778H1.00195V17H7.22417V10.7778Z" stroke="#FF8544" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 573 B |
|
After Width: | Height: | Size: 61 KiB |
@@ -0,0 +1,22 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4467_7657)">
|
||||
<path d="M6.54065 20.7133C6.54065 18.9072 5.07648 17.443 3.27033 17.443C1.46417 17.443 0 18.9072 0 20.7133C0 22.5195 1.46417 23.9837 3.27033 23.9837C5.07648 23.9837 6.54065 22.5195 6.54065 20.7133Z" fill="#17BCAF"/>
|
||||
<path d="M23.9838 16.3516C23.9838 16.7226 23.958 17.0851 23.9062 17.441C23.3841 21.0997 20.2648 23.9213 16.4767 23.9817C16.4358 23.9817 16.3948 23.9817 16.3538 23.9817H11.994C10.1884 23.9817 8.72378 22.5169 8.72378 20.7113C8.72378 19.8075 9.09048 18.9921 9.68154 18.3988C10.2748 17.8056 11.0902 17.441 11.994 17.441H16.3171C16.8974 17.441 17.4043 17.0031 17.4432 16.4228C17.4841 15.808 17.0118 15.2924 16.4121 15.2622C16.3948 15.2622 16.3754 15.2622 16.3559 15.2622H7.63439C3.78805 15.2622 0.606175 12.419 0.0798164 8.72161C0.0258865 8.36564 0 8.00109 0 7.63222C0 7.26331 0.0258865 6.89875 0.0776594 6.5428C0.60186 2.88203 3.72118 0.062559 7.50708 0C7.54807 0 7.58907 0 7.63006 0H11.9897C13.7975 0 15.2622 1.46475 15.2622 3.27033C15.2622 4.1742 14.8955 4.98961 14.3044 5.58285C13.7112 6.17608 12.8958 6.54065 11.9919 6.54065H7.66889C7.08858 6.54065 6.58163 6.97856 6.5428 7.55888C6.50182 8.17364 6.97424 8.68919 7.57394 8.71945C7.59123 8.71945 7.61061 8.71945 7.63006 8.71945H16.3516C20.1958 8.71945 23.3755 11.5626 23.904 15.26C23.9558 15.616 23.9817 15.9806 23.9817 16.3494L23.9838 16.3516Z" fill="#18FFCD"/>
|
||||
<path d="M23.984 3.27033C23.984 1.46417 22.5199 0 20.7137 0C18.9076 0 17.4434 1.46417 17.4434 3.27033C17.4434 5.07648 18.9076 6.54065 20.7137 6.54065C22.5199 6.54065 23.984 5.07648 23.984 3.27033Z" fill="#17BCAF"/>
|
||||
<path d="M20.6855 19.622C20.6855 21.9884 18.8002 23.917 16.4488 23.9817C16.4078 23.9817 16.3669 23.9817 16.3259 23.9817H11.9661C10.1605 23.9817 8.6958 22.517 8.6958 20.7114C8.6958 19.8075 9.06251 18.9921 9.65357 18.3989C10.2469 17.8057 11.0623 17.441 11.9661 17.441H16.2892C16.8694 17.441 17.3764 17.0031 17.4153 16.4229C17.4562 15.8081 16.9838 15.2925 16.3841 15.2623C18.7657 15.2904 20.6898 17.2318 20.6898 19.622H20.6855Z" fill="url(#paint0_linear_4467_7657)"/>
|
||||
<path d="M3.27237 4.35971C3.27237 1.99326 5.15778 0.0668734 7.50916 0C7.55015 0 7.59107 0 7.63206 0H11.9918C13.7974 0 15.2621 1.46475 15.2621 3.27032C15.2621 4.1742 14.8954 4.98961 14.3044 5.58285C13.7111 6.17608 12.8957 6.54065 11.9918 6.54065H7.66874C7.08847 6.54065 6.58154 6.97856 6.5427 7.55888C6.50172 8.17364 6.97414 8.68919 7.57385 8.71945C5.19229 8.69135 3.26807 6.7499 3.26807 4.35971H3.27237Z" fill="url(#paint1_linear_4467_7657)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_4467_7657" x1="8.72017" y1="15.2623" x2="20.7103" y2="15.2623" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18C2B0"/>
|
||||
<stop offset="1" stop-color="#0B5C53"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_4467_7657" x1="15.2601" y1="0" x2="3.26995" y2="0" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18C2B0"/>
|
||||
<stop offset="1" stop-color="#0B5C53"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_4467_7657">
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -34,9 +34,10 @@ import RunWorkflow from "./views/RunWorkflow.jsx";
|
||||
import Admin2 from "./views/Admin2.jsx";
|
||||
|
||||
import LoginPage from "./views/LoginPage.jsx";
|
||||
import LoginPageOld from "./views/LoginPageOld.jsx";
|
||||
|
||||
import SettingsPage from "./views/SettingsPage.jsx";
|
||||
import KeepAlive from "./views/KeepAlive.jsx";
|
||||
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
|
||||
@@ -59,6 +60,7 @@ import 'react-toastify/dist/ReactToastify.css';
|
||||
import Drift from "react-driftjs";
|
||||
|
||||
import { AppContext } from './context/ContextApi.jsx';
|
||||
import Navbar from "./components/Navbar.jsx";
|
||||
import Workflows2 from "./views/Workflows2.jsx";
|
||||
import AppExplorer from "./views/AppExplorer.jsx";
|
||||
|
||||
@@ -213,7 +215,20 @@ const App = (message, props) => {
|
||||
|
||||
{ window?.location?.pathname === "/" || window?.location?.pathname === "/training" || !(isLoggedIn && isLoaded) ? (
|
||||
<div style={{ minHeight: 68, maxHeight: 68 }}>
|
||||
<Header
|
||||
{/* <Header
|
||||
notifications={notifications}
|
||||
setNotifications={setNotifications}
|
||||
userdata={userdata}
|
||||
cookies={cookies}
|
||||
removeCookie={removeCookie}
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
setIsLoggedIn={setIsLoggedIn}
|
||||
isLoggedIn={isLoggedIn}
|
||||
curpath={curpath}
|
||||
{...props}
|
||||
/> */}
|
||||
<Navbar
|
||||
notifications={notifications}
|
||||
setNotifications={setNotifications}
|
||||
userdata={userdata}
|
||||
@@ -237,6 +252,7 @@ const App = (message, props) => {
|
||||
<div style={{ height: 60 }} />
|
||||
*/}
|
||||
<Routes>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/login"
|
||||
@@ -244,7 +260,8 @@ const App = (message, props) => {
|
||||
<LoginPage
|
||||
isLoggedIn={isLoggedIn}
|
||||
setIsLoggedIn={setIsLoggedIn}
|
||||
register={true}
|
||||
register={false}
|
||||
inregister={false}
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
setCookie={setCookie}
|
||||
@@ -254,6 +271,63 @@ const App = (message, props) => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/login2"
|
||||
element={
|
||||
<LoginPageOld
|
||||
isLoggedIn={isLoggedIn}
|
||||
setIsLoggedIn={setIsLoggedIn}
|
||||
register={false}
|
||||
inregister={false}
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
setCookie={setCookie}
|
||||
cookies={cookies}
|
||||
checkLogin={checkLogin}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/loginsetup"
|
||||
element={
|
||||
<LoginPageOld
|
||||
isLoggedIn={isLoggedIn}
|
||||
setIsLoggedIn={setIsLoggedIn}
|
||||
register={false}
|
||||
inregister={false}
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
setCookie={setCookie}
|
||||
cookies={cookies}
|
||||
checkLogin={checkLogin}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/register"
|
||||
element={
|
||||
<LoginPage
|
||||
isLoggedIn={isLoggedIn}
|
||||
setIsLoggedIn={setIsLoggedIn}
|
||||
inregister={true}
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
setCookie={setCookie}
|
||||
cookies={cookies}
|
||||
checkLogin={checkLogin}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/admin2"
|
||||
@@ -356,6 +430,7 @@ const App = (message, props) => {
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/AdminSetup"
|
||||
@@ -368,6 +443,7 @@ const App = (message, props) => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/detectionframework"
|
||||
@@ -560,6 +636,32 @@ const App = (message, props) => {
|
||||
<Route exact path="/forms/:key/run" element={<RunWorkflow serverside={false} userdata={userdata} globalUrl={globalUrl} isLoaded={isLoaded} isLoggedIn={isLoggedIn} surfaceColor={theme.palette.surfaceColor} inputColor={theme.palette.inputColor}{...props} />} />
|
||||
<Route exact path="/forms/:key" element={<RunWorkflow serverside={false} userdata={userdata} globalUrl={globalUrl} isLoaded={isLoaded} isLoggedIn={isLoggedIn} surfaceColor={theme.palette.surfaceColor} inputColor={theme.palette.inputColor}{...props} />} />
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/legal/:key"
|
||||
element={
|
||||
<Docs
|
||||
isMobile={isMobile}
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
isLoggedIn={isLoggedIn}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/legal"
|
||||
element={
|
||||
<Docs
|
||||
isMobile={isMobile}
|
||||
isLoaded={isLoaded}
|
||||
globalUrl={globalUrl}
|
||||
isLoggedIn={isLoggedIn}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/docs/:key"
|
||||
@@ -577,7 +679,6 @@ const App = (message, props) => {
|
||||
exact
|
||||
path="/docs"
|
||||
element={
|
||||
//navigate(`/docs/about`)
|
||||
<Docs
|
||||
isMobile={isMobile}
|
||||
isLoaded={isLoaded}
|
||||
|
||||
@@ -874,15 +874,18 @@ const AppAuthTab = memo((props) => {
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
style={{ color: '#1a1a1a', textTransform: 'none', backgroundColor: "#FF8444", marginLeft:"auto", borderRadius: 4,fontSize: 16, minWidth: 162, height: 40, boxShadow:'none', }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={!isCloud}
|
||||
onClick={() => setShowAppModal(true)}
|
||||
>
|
||||
Add App Auth
|
||||
</Button>
|
||||
|
||||
{isCloud ?
|
||||
<Button
|
||||
style={{ color: '#1a1a1a', textTransform: 'none', backgroundColor: "#FF8444", marginLeft:"auto", borderRadius: 4,fontSize: 16, minWidth: 162, height: 40, boxShadow:'none', }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={!isCloud}
|
||||
onClick={() => setShowAppModal(true)}
|
||||
>
|
||||
Add App Auth
|
||||
</Button>
|
||||
: null}
|
||||
</div>
|
||||
{/* <Divider
|
||||
style={{
|
||||
@@ -1217,6 +1220,8 @@ const AppAuthTab = memo((props) => {
|
||||
{editAuthenticationModal}
|
||||
{authenticationView}
|
||||
<div style={{marginTop: 50, }}>
|
||||
|
||||
{/*
|
||||
<div style={{ marginTop: 150, marginBottom: 20 }}>
|
||||
<h2 style={{ color: "#FFFFFF" }}>App Authentication Groups</h2>
|
||||
<span style={{ marginLeft: 0 }}>
|
||||
@@ -1415,13 +1420,14 @@ const AppAuthTab = memo((props) => {
|
||||
);
|
||||
}
|
||||
)}
|
||||
</List>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</List>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import CreateIcon from '@mui/icons-material/Create'
|
||||
import { toast } from 'react-toastify'
|
||||
import YAML from "yaml";
|
||||
|
||||
|
||||
const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
|
||||
const [openApiModal, setOpenApiModal] = useState(false)
|
||||
const [generateAppModal, setGenerateAppModal] = useState(false)
|
||||
@@ -59,6 +58,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
|
||||
: "1px solid rgba(255,255,255,0.3)",
|
||||
borderImage: makeFancy ? "linear-gradient(to right, #ff8544 0%, #ec517c 50%, #9c5af2 100%) 1" : "none",
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
paddingBottom: isCloud ? 0 : 175,
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -256,26 +256,27 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
|
||||
body: openApidata,
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
.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());
|
||||
});
|
||||
setValidation(false);
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson?.success === true) {
|
||||
setAppValidation(responseJson?.id)
|
||||
navigate(`/apps/new?id=${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 redirectOpenApi = () => {
|
||||
@@ -416,7 +417,7 @@ const AppCreationModal = ({ open, onClose, theme, globalUrl, isCloud }) => {
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: 3 }}>
|
||||
<div style={{ display: "flex", gap: '16px' }}>
|
||||
<div style={{ display: "flex", gap: 16, }}>
|
||||
<AppCreateButton
|
||||
text="Upload OpenAPI or Swagger"
|
||||
func={() => {
|
||||
|
||||
@@ -77,6 +77,8 @@ const Billing = memo((props) => {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [deleteAlertIndex, setDeleteAlertIndex] = useState(-1);
|
||||
const [deleteAlertVerification, setDeleteAlertVerification] = useState(false);
|
||||
const [isScale, setIsScale] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (userdata.app_execution_limit !== undefined && userdata.app_execution_usage !== undefined) {
|
||||
const percentage = (userdata.app_execution_usage / userdata.app_execution_limit) * 100;
|
||||
@@ -176,11 +178,10 @@ const Billing = memo((props) => {
|
||||
padding: 20,
|
||||
// maxWidth: 400,
|
||||
width: 340,
|
||||
height: 480,
|
||||
height: 'auto',
|
||||
// width: "100%",
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
borderRadius: theme.palette?.borderRadius * 2,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
backgroundColor: "#1e1e1e",
|
||||
borderRadius: 10,
|
||||
marginRight: 10,
|
||||
marginTop: 15,
|
||||
}
|
||||
@@ -361,7 +362,7 @@ const Billing = memo((props) => {
|
||||
var showSupport = false
|
||||
if (subscription.name.includes("default")) {
|
||||
top_text = "Custom Contract"
|
||||
newPaperstyle.border = "1px solid rgba(255,255,255,0.3)"
|
||||
// newPaperstyle.border = "1px solid rgba(255,255,255,0.3)"
|
||||
showSupport = true
|
||||
}
|
||||
|
||||
@@ -377,16 +378,17 @@ const Billing = memo((props) => {
|
||||
|
||||
if (subscription.name.includes("Scale")) {
|
||||
top_text = "Scale access"
|
||||
setIsScale(true)
|
||||
}
|
||||
|
||||
if (highlight === true) {
|
||||
// Add an "Upgrade now" button
|
||||
newPaperstyle.border = "1px solid rgba(255,255,255,0.3)"
|
||||
// newPaperstyle.border = "1px solid rgba(255,255,255,0.3)"
|
||||
}
|
||||
|
||||
if (hovered) {
|
||||
newPaperstyle.backgroundColor = "#2b2b2b"
|
||||
}
|
||||
// if (hovered) {
|
||||
// newPaperstyle.backgroundColor = "#2b2b2b"
|
||||
// }
|
||||
|
||||
const handleClickOpen = () => {
|
||||
setOpenChangeEmailBox(true);
|
||||
@@ -556,6 +558,7 @@ const Billing = memo((props) => {
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Button style={{ backgroundColor: '#2F2F2F', color: "white", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', width: 144, height: 40 }}>Current Plan</Button>
|
||||
<div style={{ display: "flex", width: 340 }}>
|
||||
{top_text === "Base Cloud Access" && userdata.has_card_available === true ?
|
||||
<Chip
|
||||
@@ -659,8 +662,8 @@ const Billing = memo((props) => {
|
||||
<TextField
|
||||
value={feature.split("Worker License: ")[1]}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
backgroundColor: theme.palette.textFieldStyle.backgroundColor,
|
||||
borderRadius: theme.palette?.textFieldStyle.borderRadius,
|
||||
}}
|
||||
id={fieldId}
|
||||
onClick={() => { }}
|
||||
@@ -2013,7 +2016,33 @@ const Billing = memo((props) => {
|
||||
</Typography>
|
||||
: null}
|
||||
|
||||
<div style={{ display: "flex", width: clickedFromOrgTab ? "100%" : "auto", overflowX: 'auto', overflowY: 'hidden', scrollbarWidth: 'thin', scrollbarColor: '#494949 #2f2f2f', height: isChildOrg ? 0 : "100%", maxWidth: 860, marginTop: 20}} >
|
||||
<div style={{ display: "flex", width: clickedFromOrgTab ? "100%" : "auto", overflowX: 'auto', overflowY: 'hidden', scrollbarWidth: 'thin', scrollbarColor: '#494949 #2f2f2f', height: isChildOrg ? 0 : "100%", marginTop: 20}} >
|
||||
<div style={{ display: "flex", flexDirection: "column", width: "100%", }}>
|
||||
{isCloud &&
|
||||
selectedOrganization.subscriptions !== undefined &&
|
||||
selectedOrganization.subscriptions !== null &&
|
||||
selectedOrganization.subscriptions.length > 0 &&
|
||||
!isChildOrg ?
|
||||
selectedOrganization.subscriptions
|
||||
.reverse()
|
||||
.map((sub, index) => {
|
||||
return (
|
||||
<SubscriptionObject
|
||||
index={index + 1}
|
||||
globalUrl={globalUrl}
|
||||
userdata={userdata}
|
||||
serverside={serverside}
|
||||
billingInfo={billingInfo}
|
||||
stripeKey={stripeKey}
|
||||
selectedOrganization={selectedOrganization}
|
||||
subscription={sub}
|
||||
highlight={true}
|
||||
/>
|
||||
)
|
||||
})
|
||||
: null}
|
||||
|
||||
<div style={{ display: "flex", flexDirection: "row", width: "100%", marginTop: 20, marginBottom: 20, maxWidth: 860, }}>
|
||||
{isCloud && billingInfo.subscription !== undefined && billingInfo.subscription !== null ? isChildOrg ? null :
|
||||
<LicencePopup
|
||||
serverside={serverside}
|
||||
@@ -2026,6 +2055,7 @@ const Billing = memo((props) => {
|
||||
isCloud={isCloud}
|
||||
userdata={userdata}
|
||||
stripeKey={stripeKey}
|
||||
isScale={isScale}
|
||||
{...props}
|
||||
/>
|
||||
: !isCloud ?
|
||||
@@ -2061,6 +2091,7 @@ const Billing = memo((props) => {
|
||||
isCloud={isCloud}
|
||||
userdata={userdata}
|
||||
stripeKey={stripeKey}
|
||||
isScale={isScale}
|
||||
/>
|
||||
|
||||
{/* <SubscriptionObject
|
||||
@@ -2084,30 +2115,9 @@ const Billing = memo((props) => {
|
||||
/> */}
|
||||
</span>
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isCloud &&
|
||||
selectedOrganization.subscriptions !== undefined &&
|
||||
selectedOrganization.subscriptions !== null &&
|
||||
selectedOrganization.subscriptions.length > 0 &&
|
||||
!isChildOrg ?
|
||||
selectedOrganization.subscriptions
|
||||
.reverse()
|
||||
.map((sub, index) => {
|
||||
return (
|
||||
<SubscriptionObject
|
||||
index={index + 1}
|
||||
globalUrl={globalUrl}
|
||||
userdata={userdata}
|
||||
serverside={serverside}
|
||||
billingInfo={billingInfo}
|
||||
stripeKey={stripeKey}
|
||||
selectedOrganization={selectedOrganization}
|
||||
subscription={sub}
|
||||
highlight={true}
|
||||
/>
|
||||
)
|
||||
})
|
||||
: null}
|
||||
{/*
|
||||
<Grid item key={index} xs={12/selectedOrganization.subscriptions.length}>
|
||||
<Card
|
||||
@@ -2153,7 +2163,6 @@ const Billing = memo((props) => {
|
||||
*/}
|
||||
</div>
|
||||
|
||||
|
||||
{/*isCloud &&
|
||||
selectedOrganization.partner_info !== undefined &&
|
||||
selectedOrganization.partner_info.reseller === true ? (
|
||||
|
||||
@@ -579,12 +579,20 @@ const EditWorkflow = (props) => {
|
||||
<Typography variant="h4" style={{ marginTop: 50, }}>
|
||||
Multi-Tenancy, Backups & Security
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" color="textSecondary" style={{ marginTop: 30, marginBottom: 10, }}>
|
||||
Multi-Tenant Workflows. Make one workflow, and keep a separate, synced copy in all your tenants. Control distributed auth, runtime locations, files, datastore keys etc. (contact support@shuffler.io if you want a demo. Please try it!)
|
||||
Control mechanisms for multi-tenancy, backups, and security.
|
||||
</Typography>
|
||||
|
||||
{userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ?
|
||||
<Typography variant="h6" style={{ marginTop: 50, }}>
|
||||
Multi-Tenant Workflows
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" color="textSecondary" style={{ marginTop: 10, marginBottom: 10, }}>
|
||||
Make one workflow, and keep a separate, synced copy in your other tenants. Control distributed auth, runtime locations, files, datastore keys etc. Can only distribute from parent org to child org. Need help trying it? <a href="https://shuffler.io/contact" target="_blank" style={{color: "#f85a3e", textDecoration: "none",}}>Contact us for a demo</a>
|
||||
</Typography>
|
||||
|
||||
{userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ?
|
||||
|
||||
userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ?
|
||||
userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ?
|
||||
<Typography variant="body2" style={{ marginTop: 10, color: "rgba(255,255,255,0.7)" }}>
|
||||
@@ -602,6 +610,7 @@ const EditWorkflow = (props) => {
|
||||
multiple
|
||||
style={{ marginTop: 10, }}
|
||||
value={innerWorkflow.suborg_distribution === undefined || innerWorkflow.suborg_distribution === null ? ["none"] : innerWorkflow.suborg_distribution}
|
||||
disabled={workflow?.parentorg_workflow !== undefined && workflow?.parentorg_workflow !== null && workflow?.parentorg_workflow.length > 0}
|
||||
onChange={(e) => {
|
||||
var newvalue = e.target.value
|
||||
if (newvalue.length > 1 && newvalue[0] === "none") {
|
||||
|
||||
@@ -1420,7 +1420,7 @@ const EnvironmentTab = memo((props) => {
|
||||
<Collapse in={listItemExpanded === index} timeout="auto" unmountOnExit>
|
||||
<Grid container justifyContent="center" style={{minWidth: 850, maxWidth: 850, }}>
|
||||
<Grid item xs={12} sm={8} md={6}>
|
||||
<div style={{minWidth: 700, maxWidth: 700, minHeight: 350, display: 'flex', justifyContent: "center", backgroundColor: "transparent", }}>
|
||||
<div style={{minWidth: 750, maxWidth: 750, minHeight: 350, display: 'flex', justifyContent: "center", backgroundColor: "transparent", }}>
|
||||
<div style={{ paddingTop: 50, paddingBottom: 100, }}>
|
||||
<Typography variant="h6">
|
||||
Self-Hosted Orborus instance
|
||||
|
||||
@@ -87,21 +87,21 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
return orgOptions.find((option) => option.name === selectedOrg);
|
||||
}, [selectedOrg, orgOptions]);
|
||||
|
||||
//With this code it is opening search bar on google chrome search bar as well which is not required
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event) => {
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === "k") {
|
||||
event.preventDefault();
|
||||
setSearchBarModalOpen((prev)=> !prev);
|
||||
}
|
||||
};
|
||||
//With this code it is opening search bar on google chrome search bar as well which is not required
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event) => {
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === "k") {
|
||||
event.preventDefault();
|
||||
setSearchBarModalOpen((prev)=> !prev);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, [setSearchBarModalOpen]);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, [setSearchBarModalOpen]);
|
||||
|
||||
const CustomPopper = (props) => {
|
||||
return (
|
||||
@@ -155,6 +155,7 @@ useEffect(() => {
|
||||
>
|
||||
{props.children}
|
||||
</Box>
|
||||
|
||||
<Link to="/admin?tab=tenants" style={hrefStyle}>
|
||||
<Box
|
||||
sx={{
|
||||
@@ -1605,6 +1606,18 @@ useEffect(() => {
|
||||
marginLeft: 5,
|
||||
}}
|
||||
>
|
||||
|
||||
{expandLeftNav &&
|
||||
<Button
|
||||
variant="outlined"
|
||||
style={{marginBottom: 15, borderWidth: 2, }}
|
||||
onClick={() => {
|
||||
window.open("https://shuffler.io/contact?category=book_a_demo", "_blank")
|
||||
}}
|
||||
>
|
||||
Book a Demo
|
||||
</Button>
|
||||
}
|
||||
<Box ref={autocompleteRef}>
|
||||
<Autocomplete
|
||||
disablePortal
|
||||
@@ -1898,8 +1911,6 @@ useEffect(() => {
|
||||
};
|
||||
|
||||
export default LeftSideBar;
|
||||
|
||||
|
||||
const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, serverside, userdata}) => {
|
||||
return (
|
||||
(
|
||||
|
||||
@@ -46,7 +46,7 @@ import { handlePayasyougo } from "../views/HandlePaymentNew.jsx"
|
||||
import Billing from "./Billing.jsx";
|
||||
|
||||
const LicencePopup = (props) => {
|
||||
const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isLoggedIn, isMobile, selectedOrganization, isCloud } = props;
|
||||
const { globalUrl, userdata, serverside, billingInfo, stripeKey, setModalOpen, isScale, isLoggedIn, isMobile, selectedOrganization, isCloud } = props;
|
||||
//const alert = useAlert();
|
||||
let navigate = useNavigate();
|
||||
const [selectedDealModalOpen, setSelectedDealModalOpen] = React.useState(false);
|
||||
@@ -165,11 +165,25 @@ const LicencePopup = (props) => {
|
||||
]
|
||||
}
|
||||
|
||||
if (userdata?.app_execution_limit >= 300000) {
|
||||
subscription.name = "Enterprise"
|
||||
subscription.currency_text = "$"
|
||||
subscription.price = typecost_single
|
||||
subscription.limit = userdata?.app_execution_limit
|
||||
subscription.interval = "app run / month"
|
||||
subscription.features = [
|
||||
"Includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. ",
|
||||
"Multi-Tenancy and Region-Selection",
|
||||
"And all other features from /pricing",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
var newPaperstyle = JSON.parse(JSON.stringify(paperStyle))
|
||||
if (subscription.name === "Enterprise" && subscription.active === true) {
|
||||
top_text = "Current Plan"
|
||||
top_text = "Enterprise Plan"
|
||||
|
||||
newPaperstyle.border = "1px solid #f85a3e"
|
||||
// newPaperstyle.border = "1px solid #f85a3e"
|
||||
}
|
||||
|
||||
var showSupport = false
|
||||
@@ -255,7 +269,7 @@ const LicencePopup = (props) => {
|
||||
style={{ borderRadius: theme.palette?.borderRadius, }}
|
||||
placement="bottom"
|
||||
>
|
||||
<div style={{}}>
|
||||
<div style={{ backgroundColor: "#1e1e1e"}}>
|
||||
<Paper
|
||||
style={newPaperstyle}
|
||||
// onMouseEnter={() => setHovered(true)}
|
||||
@@ -343,9 +357,9 @@ const LicencePopup = (props) => {
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Button style={{ backgroundColor: '#2F2F2F', color: "white", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', width: 144, height: 40 }}>Current Plan</Button>
|
||||
{subscription.active === true && !isScale && <Button style={{ backgroundColor: '#2F2F2F', color: "white", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', width: 144, height: 40 }}>Current Plan</Button> }
|
||||
<div style={{ display: "flex" }}>
|
||||
{top_text === "Base Cloud Access" && userdata.has_card_available === true ?
|
||||
{top_text === "Base Cloud Access" && userdata.has_card_available === true && !isScale ?
|
||||
<Chip
|
||||
style={{
|
||||
backgroundColor: "#f86a3e",
|
||||
@@ -511,7 +525,10 @@ const LicencePopup = (props) => {
|
||||
"While you have a card attached to your account, Shuffle will no longer prevent workflows from running. Billing will occur at the start of each month."
|
||||
:
|
||||
isCloud ?
|
||||
`You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.`
|
||||
userdata?.app_execution_limit && userdata?.app_execution_limit >= 300000 ?
|
||||
"You have subscribed to the Enterprise plan, which includes " + (userdata?.app_execution_limit/1000) + "K app runs/month. You can increase the limit by upgrading current plan. Contact support@shuffler.io for more information." :
|
||||
`You are not subscribed to any plan and are using the free plan with max 10,000 app runs per month. Upgrade to deactivate this limit.`
|
||||
|
||||
:
|
||||
`You are not subscribed to any plan and are using the free, open source plan. This plan has no enforced limits, but scale issues may occur due to CPU congestion.`
|
||||
}
|
||||
@@ -699,8 +716,33 @@ const LicencePopup = (props) => {
|
||||
setCalculatedCost("$960")
|
||||
setSelectedValue(8)
|
||||
} else {
|
||||
setCalculatedCost("$960")
|
||||
setSelectedValue(300)
|
||||
if (userdata && userdata?.app_execution_limit) {
|
||||
if (userdata.app_execution_limit >= 300000 && userdata.app_execution_limit < 400000) {
|
||||
setSelectedValue(400)
|
||||
setCalculatedCost("$1280")
|
||||
}else if (userdata?.app_execution_limit >= 400000 && userdata?.app_execution_limit < 500000) {
|
||||
setSelectedValue(500)
|
||||
setCalculatedCost("$1600")
|
||||
} else if (userdata?.app_execution_limit >= 500000 && userdata?.app_execution_limit < 600000) {
|
||||
setSelectedValue(600)
|
||||
setCalculatedCost("$1920")
|
||||
} else if (userdata?.app_execution_limit >= 600000 && userdata?.app_execution_limit < 700000) {
|
||||
setSelectedValue(700)
|
||||
setCalculatedCost("$2240")
|
||||
} else if (userdata?.app_execution_limit >= 700000 && userdata?.app_execution_limit < 800000) {
|
||||
setSelectedValue(800)
|
||||
setCalculatedCost("$2560")
|
||||
} else if (userdata?.app_execution_limit >= 800000 && userdata?.app_execution_limit < 900000) {
|
||||
setSelectedValue(900)
|
||||
setCalculatedCost("$2880")
|
||||
}else {
|
||||
setCalculatedCost("$960")
|
||||
setSelectedValue(300)
|
||||
}
|
||||
}else {
|
||||
setCalculatedCost("$960")
|
||||
setSelectedValue(300)
|
||||
}
|
||||
}
|
||||
}, [shuffleVariant])
|
||||
|
||||
@@ -864,10 +906,14 @@ const LicencePopup = (props) => {
|
||||
return
|
||||
}
|
||||
|
||||
const priceItem = window.location.origin === "https://shuffler.io" ?
|
||||
shuffleVariant === 0 ? "app_executions" : "cores"
|
||||
:
|
||||
shuffleVariant === 0 ? "price_1PZPSSEJjT17t98NLJoTMYja" : "price_1PZPQuEJjT17t98N3yORUtd9"
|
||||
const priceItem =
|
||||
window.location.origin === "https://shuffler.io/" || "https://sandbox.shuffler.io/"
|
||||
? shuffleVariant === 0
|
||||
? "app_executions"
|
||||
: "cores"
|
||||
: shuffleVariant === 0
|
||||
? "price_1PZPSSEJjT17t98NLJoTMYja"
|
||||
: "price_1PZPQuEJjT17t98N3yORUtd9";
|
||||
|
||||
const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success`
|
||||
const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure`
|
||||
@@ -973,7 +1019,7 @@ const LicencePopup = (props) => {
|
||||
</span>
|
||||
: null}
|
||||
|
||||
{isCloud &&
|
||||
{/* {isCloud &&
|
||||
selectedOrganization.subscriptions !== undefined &&
|
||||
selectedOrganization.subscriptions !== null &&
|
||||
selectedOrganization.subscriptions.length > 0 ?
|
||||
@@ -994,7 +1040,7 @@ const LicencePopup = (props) => {
|
||||
/>
|
||||
)
|
||||
})
|
||||
: null}
|
||||
: null} */}
|
||||
</Grid>
|
||||
<Grid item xs={8}>
|
||||
<Grid style={{}}>
|
||||
@@ -1002,12 +1048,12 @@ const LicencePopup = (props) => {
|
||||
<Card style={{
|
||||
padding: 20,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
border: "1px solid #f85a3e",
|
||||
border: !isScale ? "1px solid #f85a3e" : 'none',
|
||||
}}>
|
||||
<div>
|
||||
<Button style={{ backgroundColor: 'rgba(255, 132, 68, 0.2)', color: "#FF8444", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', }}
|
||||
{ !isScale && <Button style={{ backgroundColor: 'rgba(255, 132, 68, 0.2)', color: "#FF8444", textTransform: "capitalize", borderRadius: 200, boxShadow: 'none', }}
|
||||
variant="contained"
|
||||
color="primary">Recommended </Button>
|
||||
color="primary">Recommended </Button> }
|
||||
|
||||
</div>
|
||||
<Typography variant="h6" style={{ marginTop: 10, marginBottom: 10, flex: 5, }}>{shuffleVariant === 1 ? "Scale" : "Enterprise"}</Typography>
|
||||
|
||||
@@ -359,7 +359,7 @@ const RuntimeDebugger = (props) => {
|
||||
imageSource = "/images/no_image.png"
|
||||
}
|
||||
}else {
|
||||
if (userdata.active_org.image?.length > 0){
|
||||
if (userdata?.active_org.image?.length > 0){
|
||||
imageSource = userdata?.active_org?.image
|
||||
}else {
|
||||
imageSource = "/images/no_image.png"
|
||||
|
||||
@@ -497,7 +497,9 @@ const TenantsTab = memo((props) => {
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
toast(responseJson.reason);
|
||||
toast.error(responseJson.reason, {
|
||||
autoClose: 5000,
|
||||
})
|
||||
} else {
|
||||
toast("Failed creating suborg. Please try again");
|
||||
}
|
||||
@@ -781,9 +783,9 @@ const TenantsTab = memo((props) => {
|
||||
<div style={{height: "100%", maxHeight: 1700, overflowY: "auto",overflowX: 'hidden', scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin'}}>
|
||||
<div style={{ height: "100%", width: "calc(100% - 20px)", scrollbarColor: '#494949 transparent', scrollbarWidth: 'thin' }}>
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Organizations</h2>
|
||||
<h2 style={{ marginBottom: 8, marginTop: 0, color: "#ffffff" }}>Tenants</h2>
|
||||
<span style={{ color: textColor }}>
|
||||
Control sub organizations (tenants)! {" "}
|
||||
Create, manage and change to sub-organizations (tenants)! {" "}
|
||||
{isCloud
|
||||
? "You can only make a sub organization if you are a customer of shuffle or running a POC of the platform. Please contact support@shuffler.io to try it out."
|
||||
: ''}
|
||||
|
||||
@@ -1557,10 +1557,12 @@ const UserManagmentTab = memo((props) => {
|
||||
style={{ display:'table-cell', verticalAlign: 'middle' }}
|
||||
/>
|
||||
|
||||
{/*
|
||||
<ListItemText
|
||||
primary={data.active ? "True" : "False"}
|
||||
style={{display:'table-cell',verticalAlign: 'middle' , padding: "8px", textAlign: "center", color: data.active ? "#02CB70" : "#F53434" }}
|
||||
/>
|
||||
*/}
|
||||
|
||||
<ListItemText
|
||||
primary={
|
||||
@@ -1573,7 +1575,6 @@ const UserManagmentTab = memo((props) => {
|
||||
style={{ display:'table-cell',verticalAlign: 'middle', padding: "8px", }}
|
||||
/>
|
||||
|
||||
{/*
|
||||
<ListItemText
|
||||
primary={
|
||||
data?.mfa_info !== undefined &&
|
||||
@@ -1584,7 +1585,6 @@ const UserManagmentTab = memo((props) => {
|
||||
}
|
||||
style={{ display:'table-cell', verticalAlign: 'middle',padding: "8px", color: data.mfa_info.active ? "#02CB70" : "#F53434" }}
|
||||
/>
|
||||
*/}
|
||||
|
||||
{selectedOrganization?.child_orgs !== undefined &&
|
||||
selectedOrganization?.child_orgs !== null &&
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { createContext, useState, useEffect } from 'react';
|
||||
import React, { createContext, useState, useEffect } from 'react';
|
||||
export const Context = createContext();
|
||||
|
||||
export const AppContext =(props) => {
|
||||
export const AppContext = (props) => {
|
||||
const { serverside } = props
|
||||
|
||||
const currentLocation = window?.location?.pathname;
|
||||
const currentLocation = serverside === true ? "" : window?.location?.pathname;
|
||||
|
||||
// Left side bar global states
|
||||
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
|
||||
const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(currentLocation?.includes('/workflows/') ? false : true)
|
||||
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
||||
const [windowWidth, setWindowWidth] = useState(serverside === true ? 100 : window.innerWidth);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentLocation?.includes('/workflows/') && leftSideBarOpenByClick === true) {
|
||||
@@ -18,6 +19,10 @@ export const AppContext =(props) => {
|
||||
|
||||
//Calculate window width
|
||||
useEffect(() => {
|
||||
if (serverside === true) {
|
||||
return
|
||||
}
|
||||
|
||||
const handleResize = () => {
|
||||
setWindowWidth(window?.innerWidth);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React, { useState } from "react";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import {
|
||||
CircularProgress,
|
||||
@@ -20,11 +22,8 @@ const surfaceColor = "#27292D";
|
||||
const inputColor = "#383B40";
|
||||
|
||||
const boxStyle = {
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
backgroundColor: surfaceColor,
|
||||
padding: 40,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
};
|
||||
|
||||
const useStyles = makeStyles({
|
||||
@@ -41,8 +40,10 @@ const AdminAccount = (props) => {
|
||||
const [firstRequest, setFirstRequest] = useState(true);
|
||||
const [loginLoading, setLoginLoading] = useState(false);
|
||||
|
||||
|
||||
// Used to swap from login to register. True = login, false = register
|
||||
const register = true;
|
||||
let navigate = useNavigate();
|
||||
|
||||
const classes = useStyles();
|
||||
// Error messages etc
|
||||
@@ -70,13 +71,16 @@ const AdminAccount = (props) => {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
if (responseJson.reason === "redirect") {
|
||||
window.location.pathname = "/login";
|
||||
setTimeout(() => {
|
||||
window.location.pathname = "/login";
|
||||
}, 2500)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginInfo("Error in userdata: ", error);
|
||||
setLoginInfo("Error in userdata (1): ", error);
|
||||
navigate("/loginsetup")
|
||||
});
|
||||
};
|
||||
|
||||
@@ -108,13 +112,17 @@ const AdminAccount = (props) => {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
setLoginInfo("Successful register :)");
|
||||
window.location.pathname = "/login";
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.pathname = "/login";
|
||||
}, 2500)
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginLoading(false);
|
||||
setLoginInfo("Error in userdata: ", error);
|
||||
setLoginInfo("Error in userdata (2): ", error);
|
||||
setLoginLoading(false)
|
||||
navigate("/loginsetup")
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -575,11 +575,17 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}, [editWorkflowModalOpen])
|
||||
|
||||
useEffect(() => {
|
||||
if(selectedTrigger?.trigger_type === "SUBFLOW"){
|
||||
// Check if selectedTriggerIndex is a number >= 0
|
||||
if (isNaN(selectedTriggerIndex) || selectedTriggerIndex < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (selectedTrigger?.trigger_type === "SUBFLOW" && selectedTriggerIndex !== undefined && selectedTriggerIndex !== null && workflow?.triggers[selectedTriggerIndex].parameters.length > 1) {
|
||||
setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[1].value || "")
|
||||
} else if(selectedTrigger?.trigger_type === "USERINPUT"){
|
||||
} else if (selectedTrigger?.trigger_type === "USERINPUT" && selectedTriggerIndex !== undefined && selectedTriggerIndex !== null && workflow?.triggers[selectedTriggerIndex].parameters.length > 0) {
|
||||
setSelectedTriggerValue(workflow.triggers[selectedTriggerIndex].parameters[0].value || "Do you want to continue the workflow? Start parameters: $exec")
|
||||
}
|
||||
|
||||
}, [selectedTriggerIndex, selectedTrigger, workflow])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -948,11 +954,15 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (workflow.actions?.length == 1) {
|
||||
if (workflow.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") {
|
||||
if (workflow?.actions?.length == 1) {
|
||||
if (workflow?.actions[0].app_id == "3e320a20966d33c9b7e6790b2705f0bf") {
|
||||
setWorkflowAsCode(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (workflow?.suborg_distribution !== undefined && workflow?.suborg_distribution !== null && workflow?.suborg_distribution.length > 0) {
|
||||
getChildWorkflows(workflow.id)
|
||||
}
|
||||
}, [workflow]);
|
||||
|
||||
// Event for making sure app is correct
|
||||
@@ -1288,56 +1298,6 @@ const AngularWorkflow = (defaultprops) => {
|
||||
return
|
||||
}
|
||||
|
||||
//console.log("Failed in trigger selection: ", selectedTriggerIndex, "Trigger: ", selectedTrigger)
|
||||
|
||||
var found = null
|
||||
try {
|
||||
for (var key in workflows) {
|
||||
const curworkflow = workflows[key]
|
||||
const curtrigger = curworkflow?.triggers[selectedTriggerIndex]
|
||||
if (curtrigger === undefined || curtrigger === null) {
|
||||
console.log("Failed in trigger selection (1): ", curworkflow)
|
||||
continue
|
||||
}
|
||||
|
||||
if (curtrigger?.parameters === undefined || curtrigger?.parameters === null || curtrigger?.parameters.length === 0) {
|
||||
console.log("Failed in trigger selection (2): ", curworkflow)
|
||||
continue
|
||||
}
|
||||
|
||||
if (curtrigger?.parameters[0] === undefined || curtrigger?.parameters[0] === null || curtrigger?.parameters[0].value === undefined || curtrigger?.parameters[0].value === null) {
|
||||
console.log("Failed in trigger selection (3): ", curworkflow)
|
||||
continue
|
||||
}
|
||||
|
||||
if (curtrigger?.parameters[0].value === selectedTrigger?.parameters[0]?.value) {
|
||||
found = curworkflow
|
||||
setSubworkflow(curworkflow)
|
||||
}
|
||||
}
|
||||
|
||||
if (found !== null) {
|
||||
setSubworkflow(found)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Failed in trigger selection (4): ", e)
|
||||
//return
|
||||
}
|
||||
|
||||
if (found) {
|
||||
const startNode = found.actions?.find((action) => action.id === workflow?.triggers[selectedTriggerIndex]?.parameters[3]?.value)
|
||||
setSubworkflowStartnode(startNode)
|
||||
}
|
||||
|
||||
/*
|
||||
// Multi-tenant sometimes gives us shit :(
|
||||
if (selectedTrigger === undefined || selectedTrigger === null || selectedTrigger.id === undefined || selectedTrigger.id === null && selectedTriggerIndex >= 0) {
|
||||
if (workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers.length > selectedTriggerIndex) {
|
||||
setSelectedTrigger(workflow.triggers[selectedTriggerIndex])
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Check if the running state is correct or not according to allTriggers
|
||||
if (allTriggers !== undefined && allTriggers !== null) {
|
||||
// Find the active trigger
|
||||
@@ -1545,6 +1505,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0) {
|
||||
headers["Org-Id"] = workflow.org_id
|
||||
}
|
||||
|
||||
setWorkflows([])
|
||||
|
||||
fetch(globalUrl + "/api/v1/workflows?subflow=true", {
|
||||
method: "GET",
|
||||
@@ -1562,7 +1524,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
if (responseJson !== undefined) {
|
||||
|
||||
// Sets up subflow trigger with the right info
|
||||
if (trigger_index > -1) {
|
||||
if (isNaN(trigger_index) === false && trigger_index > -1) {
|
||||
|
||||
var baseSubflow = {}
|
||||
const trigger = workflow.triggers[trigger_index];
|
||||
@@ -1592,10 +1554,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
if (Object.getOwnPropertyNames(baseSubflow).length > 0) {
|
||||
const foundAction = baseSubflow.actions.find(action => action?.id === param.value)
|
||||
if (foundAction !== null && foundAction !== undefined) {
|
||||
setSubworkflowStartnode(foundAction);
|
||||
setSubworkflowStartnode(foundAction)
|
||||
}
|
||||
} else {
|
||||
setSubworkflowStartnode(param.value);
|
||||
setSubworkflowStartnode(param.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2540,18 +2502,25 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
*/
|
||||
|
||||
// FIXME: What is this?
|
||||
if (cy !== undefined && cy !== null) {
|
||||
// scale: 0.3,
|
||||
// bg: "#27292d",
|
||||
const cyImageData = cy.png({
|
||||
output: "base64uri",
|
||||
maxWidth: 480,
|
||||
maxHeight: 270,
|
||||
})
|
||||
if (cy?.png !== undefined && cy?.png !== null) {
|
||||
try {
|
||||
const cyImageData = cy.png({
|
||||
output: "base64uri",
|
||||
maxWidth: 480,
|
||||
maxHeight: 270,
|
||||
})
|
||||
|
||||
if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) {
|
||||
useworkflow.image = cyImageData
|
||||
}
|
||||
if (cyImageData !== undefined && cyImageData !== null && cyImageData.length > 0) {
|
||||
useworkflow.image = cyImageData
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Failed to get image data: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (useworkflow.id === undefined || useworkflow.id === null || useworkflow.id.length === 0) {
|
||||
@@ -3948,7 +3917,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
apps.push(responseJson.actions[index]);
|
||||
}
|
||||
|
||||
console.log("Setting used subflow apps: ", apps)
|
||||
//console.log("Setting used subflow apps: ", apps)
|
||||
setUsedSubflowApps(apps);
|
||||
|
||||
return apps
|
||||
@@ -3961,6 +3930,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
const findWorkflowDiff = (parentWorkflow, childWorkflow) => {
|
||||
// Ensures new memory is used
|
||||
parentWorkflow = JSON.parse(JSON.stringify(parentWorkflow))
|
||||
childWorkflow = JSON.parse(JSON.stringify(childWorkflow))
|
||||
|
||||
var diff = {
|
||||
"different": false,
|
||||
"environment": false,
|
||||
@@ -3978,9 +3951,13 @@ const AngularWorkflow = (defaultprops) => {
|
||||
return diff
|
||||
}
|
||||
|
||||
|
||||
var parentEnvironment = ""
|
||||
var childEnvironment = ""
|
||||
|
||||
if (parentWorkflow.triggers !== undefined && parentWorkflow.triggers !== null && parentWorkflow.triggers.length > 0) {
|
||||
parentWorkflow.actions = parentWorkflow.actions.concat(parentWorkflow.triggers)
|
||||
}
|
||||
|
||||
for (var parentKey in parentWorkflow.actions) {
|
||||
const parentAction = parentWorkflow.actions[parentKey]
|
||||
if (parentAction.environment !== undefined && parentAction.environment !== null && parentAction.environment !== "") {
|
||||
@@ -3992,6 +3969,19 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
var found = false
|
||||
if (childWorkflow.triggers !== undefined && childWorkflow.triggers !== null && childWorkflow.triggers.length > 0) {
|
||||
for (var triggerKey in childWorkflow.triggers) {
|
||||
if (childWorkflow.triggers[triggerKey].replacement_for_trigger !== parentAction.id) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Rewrapping the ID just in case
|
||||
childWorkflow.triggers[triggerKey].id = childWorkflow.triggers[triggerKey].replacement_for_trigger
|
||||
childWorkflow.actions.push(childWorkflow.triggers[triggerKey])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for (var childKey in childWorkflow.actions) {
|
||||
const childAction = childWorkflow.actions[childKey]
|
||||
if (childAction.environment !== undefined && childAction.environment !== null && childAction.environment !== "") {
|
||||
@@ -4038,6 +4028,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
continue
|
||||
}
|
||||
|
||||
var parentworkflow = ""
|
||||
var parentstartnode = ""
|
||||
var childworkflow = ""
|
||||
var childstartnode = ""
|
||||
for (var parentParamIndex in parentAction.parameters) {
|
||||
const parentParam = parentAction.parameters[parentParamIndex]
|
||||
for (var childParamIndex in childAction.parameters) {
|
||||
@@ -4046,20 +4040,38 @@ const AngularWorkflow = (defaultprops) => {
|
||||
continue
|
||||
}
|
||||
|
||||
if (childParam.name === "workflow" || childParam.name === "subflow") {
|
||||
parentworkflow = parentParam.value
|
||||
childworkflow = childParam.value
|
||||
continue
|
||||
}
|
||||
|
||||
if (childParam.name == "startnode") {
|
||||
parentstartnode = parentParam.value
|
||||
childstartnode = childParam.value
|
||||
continue
|
||||
}
|
||||
|
||||
if (childParam.value !== parentParam.value) {
|
||||
actionDiff.parameters.push(childParam.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parentworkflow !== childworkflow && parentstartnode !== childstartnode) {
|
||||
actionDiff.parameters.push("subflow")
|
||||
}
|
||||
}
|
||||
|
||||
if (actionDiff.parameters.length > 0) {
|
||||
actionDiff.params = true
|
||||
}
|
||||
|
||||
/*
|
||||
if (!found) {
|
||||
actionDiff.new = true
|
||||
}
|
||||
*/
|
||||
|
||||
if (actionDiff !== undefined && actionDiff !== null && Object.keys(actionDiff).length > 1) {
|
||||
actionDiff.label = parentAction.label.replaceAll("_", " ")
|
||||
@@ -6352,11 +6364,15 @@ const AngularWorkflow = (defaultprops) => {
|
||||
if (workflow.org_id !== undefined && workflow.org_id !== null && workflow.org_id.length > 0 && originalWorkflow.org_id !== undefined && originalWorkflow.org_id !== null && originalWorkflow.org_id.length > 0 && workflow.org_id === originalWorkflow.org_id) {
|
||||
// Allows a parent workflow to control the schedule
|
||||
} else if (data.replacement_for_trigger !== undefined && data.replacement_for_trigger !== null && data.replacement_for_trigger.length > 0) {
|
||||
toast.warning("This schedule is controlled by the parent workflow. If you want additional schedule control, please add a custom schedule to this workflow.", {
|
||||
autoClose: 30000,
|
||||
})
|
||||
event.target.unselect()
|
||||
return
|
||||
|
||||
// No custom control on cloud
|
||||
if (isCloud) {
|
||||
toast.warning("This schedule is controlled by the parent workflow. If you want additional schedule control, please add a custom schedule to this workflow.", {
|
||||
autoClose: 30000,
|
||||
})
|
||||
event.target.unselect()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
} else if (data.app_name === "Webhook" && trigger_index >= 0) {
|
||||
@@ -6932,7 +6948,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
)
|
||||
if (targetnode !== -1) {
|
||||
if (workflow.triggers[targetnode].app_name === "User Input" || workflow.triggers[targetnode].app_name === "Shuffle Workflow" || workflow.triggers[targetnode].app_name === "Shuffle Subflow") {
|
||||
console.log("User Input or Shuffle Workflow")
|
||||
//console.log("User Input or Shuffle Workflow")
|
||||
} else {
|
||||
toast("Can't have triggers as target of branch")
|
||||
event.target.remove()
|
||||
@@ -9264,11 +9280,15 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
insertedNodes = insertedNodes.concat(newedges);
|
||||
setWorkflow(inputworkflow);
|
||||
setWorkflow(inputworkflow)
|
||||
|
||||
// Reset view for cytoscape
|
||||
if (cy !== undefined && cy !== null) {
|
||||
cy.add(insertedNodes)
|
||||
try {
|
||||
cy.add(insertedNodes)
|
||||
} catch (error) {
|
||||
console.log("Error adding nodes to cytoscape (6): ", error)
|
||||
}
|
||||
|
||||
try {
|
||||
cy.fit(null, 250)
|
||||
@@ -9347,12 +9367,6 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
}
|
||||
|
||||
//if (selectedNode.data("id") === selectedAction.id) {
|
||||
// setSelectedApp({});
|
||||
// setSelectedAction({});
|
||||
//setSelectedTrigger({});
|
||||
//setSelectedTriggerIndex({});
|
||||
//}
|
||||
const parsedSelection = cy.$(":selected");
|
||||
if (selectedNode.data().decorator === true && selectedNode.data("type") !== "COMMENT") {
|
||||
toast("This node can't be deleted.");
|
||||
@@ -9552,7 +9566,6 @@ const AngularWorkflow = (defaultprops) => {
|
||||
if (firstrequest) {
|
||||
setFirstrequest(false)
|
||||
getWorkflow(props.match.params.key, {})
|
||||
getChildWorkflows(props.match.params.key)
|
||||
getRevisionHistory(props.match.params.key)
|
||||
loadTriggers()
|
||||
getApps()
|
||||
@@ -9785,9 +9798,14 @@ const AngularWorkflow = (defaultprops) => {
|
||||
toast("Successfully stopped schedule");
|
||||
}
|
||||
|
||||
workflow.triggers[triggerindex].status = "stopped";
|
||||
trigger.status = "stopped";
|
||||
setSelectedTrigger(trigger);
|
||||
if (triggerindex !== undefined && triggerindex !== null && triggerindex >= 0) {
|
||||
workflow.triggers[triggerindex].status = "stopped";
|
||||
}
|
||||
|
||||
//trigger.status = "stopped";
|
||||
//console.log("TRIGGER: ", trigger)
|
||||
//setSelectedTrigger(trigger);
|
||||
|
||||
setWorkflow(workflow);
|
||||
saveWorkflow(workflow)
|
||||
|
||||
@@ -11446,7 +11464,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
var type = "app"
|
||||
const baseImage = <LibraryBooksIcon />
|
||||
return (
|
||||
<div style={{ position: "relative", marginTop: 15, marginLeft: 0, marginRight: 10, position: "absolute", color: "white", zIndex: 1001, backgroundColor: theme.palette.inputColor, minWidth: leftBarSize - 10, maxWidth: leftBarSize - 10, boxShadows: "none", overflowX: "hidden", }}>
|
||||
<div style={{ position: "relative", marginTop: 15, marginLeft: 0, marginRight: 10, position: "absolute", color: "white", zIndex: 1001, backgroundColor: theme.palette.inputColor, minWidth: leftBarSize+30, maxWidth: 340, boxShadows: "none", overflowX: "hidden", }}>
|
||||
<List style={{ backgroundColor: theme.palette.inputColor, }}>
|
||||
{hits.length === 0 ?
|
||||
<ListItem style={outerlistitemStyle}>
|
||||
@@ -11736,7 +11754,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
Click one of the relevant public apps below to Activate it for your organization.
|
||||
Click one of the public apps below to Activate it for your organization.
|
||||
</Typography>
|
||||
<InstantSearch searchClient={searchClient} indexName="appsearch" onClick={() => {
|
||||
console.log("CLICKED")
|
||||
@@ -14637,20 +14655,19 @@ const AngularWorkflow = (defaultprops) => {
|
||||
cy.add(cybranch);
|
||||
}
|
||||
|
||||
console.log("Value to be set: ", e.target.value);
|
||||
try {
|
||||
workflow.triggers[
|
||||
selectedTriggerIndex
|
||||
].parameters[3].value = e.target.value.id;
|
||||
} catch {
|
||||
workflow.triggers[selectedTriggerIndex].parameters[3] =
|
||||
{
|
||||
name: "startnode",
|
||||
value: e.target.value.id,
|
||||
};
|
||||
workflow.triggers[selectedTriggerIndex].parameters[3].value = e.target.value.id
|
||||
} catch(e) {
|
||||
console.log("Error: ", e)
|
||||
workflow.triggers[selectedTriggerIndex].parameters[3] =
|
||||
{
|
||||
name: "startnode",
|
||||
value: e.target.value.id,
|
||||
};
|
||||
}
|
||||
|
||||
setWorkflow(workflow);
|
||||
setWorkflow(workflow)
|
||||
setUpdate(Math.random())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14934,12 +14951,15 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: "10" }}>
|
||||
<b>Select a workflow to execute </b>
|
||||
<b>Select a workflow to run</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{workflow.triggers[selectedTriggerIndex].parameters[0].value
|
||||
.length === 0 ? null : workflow.triggers[selectedTriggerIndex]
|
||||
.parameters[0].value === props.match.params.key ? null : (
|
||||
.parameters[0].value === props.match.params.key ?
|
||||
null
|
||||
: (
|
||||
<div style={{ marginLeft: 5, flex: 1 }}>
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
@@ -14960,8 +14980,9 @@ const AngularWorkflow = (defaultprops) => {
|
||||
|
||||
{workflows === undefined ||
|
||||
workflows === null ||
|
||||
workflows.length === 0 ? null : (
|
||||
|
||||
workflows.length === 0 ?
|
||||
<CircularProgress color="secondary" style={{ margin: "auto", marginTop: 10, width: 25, height: 25, }} />
|
||||
: (
|
||||
<Autocomplete
|
||||
id="subflow_search"
|
||||
autoHighlight
|
||||
@@ -15035,7 +15056,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
|
||||
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette?.borderRadius, }} />
|
||||
<img src={data.image} alt={data.name} style={{ backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, maxWidth: 285, borderRadius: theme.palette?.borderRadius, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Choose Subflow '{data.name}'
|
||||
@@ -15095,6 +15116,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
<b>Select the Startnode</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Autocomplete
|
||||
id="subflow_node_search"
|
||||
autoHighlight
|
||||
@@ -18217,6 +18239,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
value={workflow.org_id}
|
||||
disabled={savingState !== 0 || suborgWorkflows?.length === 0 || allTriggers === undefined}
|
||||
onChange={(e) => {
|
||||
if (cy !== undefined && cy !== null) {
|
||||
cy.nodes().unselect()
|
||||
}
|
||||
|
||||
if (lastSaved === false && originalWorkflow.id === workflow.id) {
|
||||
setSuborgWorkflows([])
|
||||
|
||||
|
||||
@@ -2657,12 +2657,12 @@ const AppCreator = (defaultprops) => {
|
||||
if (responseJson.reason !== undefined) {
|
||||
setErrorCode(responseJson.reason);
|
||||
|
||||
if (responseJson.extra === undefined && responseJson.extra === null) {
|
||||
toast("Failed to verify: " + responseJson.reason);
|
||||
}
|
||||
toast.error("Failed to build: " + responseJson.reason, {
|
||||
autoClose: 10000
|
||||
})
|
||||
}
|
||||
} else {
|
||||
toast("Successfully uploaded openapi");
|
||||
toast.success("Successfully built openapi app! Added job to rebuild it in your hybrid runtime locations (Orborus).");
|
||||
if (window.location.pathname.includes("/new")) {
|
||||
if (responseJson.id !== undefined && responseJson.id !== null) {
|
||||
window.location = `/apps/edit/${responseJson.id}`;
|
||||
|
||||
@@ -3866,13 +3866,13 @@ const AppExplorer = (props) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{ marginBottom: 0 }}>
|
||||
<DialogTitle style={{ marginBottom: 0, padding: 50, paddingBottom: 0, }}>
|
||||
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
|
||||
Are you sure you want to PUBLISH this app?
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent
|
||||
style={{ color: "rgba(255,255,255,0.65)", textAlign: "center" }}
|
||||
style={{ color: "rgba(255,255,255,0.65)", textAlign: "center", padding: 50, }}
|
||||
>
|
||||
<div>
|
||||
<Typography variant="body1" style={{ marginBottom: 20 }}>
|
||||
@@ -4120,7 +4120,7 @@ const AppExplorer = (props) => {
|
||||
</Button>
|
||||
)}
|
||||
{appType === 1 ? null :
|
||||
creatorProfile.self === true || userdata.support === true ?
|
||||
userdata?.support || userdata?.id === app?.owner || (userdata?.admin === "true" && userdata?.active_org?.id === app?.reference_org) || app?.contributors?.includes(userdata?.id) || creatorProfile?.self === true ?
|
||||
<Tooltip title="Edit App" placement="top">
|
||||
<Button
|
||||
variant="outlined"
|
||||
@@ -4167,9 +4167,9 @@ const AppExplorer = (props) => {
|
||||
Explore API
|
||||
</Button>
|
||||
</a>
|
||||
{(appdata?.owner === userdata?.id) ? (
|
||||
<Select
|
||||
value={sharingConfiguration}
|
||||
disabled={!isCloud}
|
||||
onChange={(event) => {
|
||||
if ((appdata.owner !== userdata.id) && (!userdata.support)) {
|
||||
toast("You're not the owner of this app. You can't update it.");
|
||||
@@ -4222,7 +4222,6 @@ const AppExplorer = (props) => {
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
): null }
|
||||
</div>
|
||||
) : (
|
||||
<a
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect, useContext, useCallback, memo, useMemo, useRef } from "react";
|
||||
import theme from "../theme.jsx";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
TextField, Button, Typography, MenuItem, Select, Tabs, Tab, Zoom,
|
||||
@@ -590,7 +591,7 @@ const Hits = ({
|
||||
<div style={{
|
||||
flex: 1
|
||||
}}>
|
||||
{hoverEffect === index && isCloud ? (
|
||||
{hoverEffect === index ? (
|
||||
<div>
|
||||
{data.tags && (
|
||||
<Tooltip
|
||||
@@ -764,7 +765,7 @@ const SearchBox = ({ refine, searchQuery, setSearchQuery }) => {
|
||||
<TextField
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Search for apps"
|
||||
placeholder="Search from 2500+ public apps"
|
||||
value={localQuery}
|
||||
id="shuffle_search_field"
|
||||
inputRef={inputRef}
|
||||
@@ -2009,7 +2010,7 @@ const Apps2 = (props) => {
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label="Discover Apps"
|
||||
label="Discover Public Apps"
|
||||
style={{
|
||||
...tabStyle,
|
||||
marginRight: 0,
|
||||
@@ -2028,7 +2029,7 @@ const Apps2 = (props) => {
|
||||
<TextField
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Search for apps"
|
||||
placeholder={currTab === 1 ? "Search your apps" : "Search org apps"}
|
||||
disabled={!isLoggedIn}
|
||||
value={searchQuery}
|
||||
id="shuffle_search_field"
|
||||
|
||||
@@ -5,7 +5,7 @@ import theme from '../theme.jsx';
|
||||
import ReactJson from "react-json-view-ssr";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { BrowserView, MobileView } from "react-device-detect";
|
||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||
import { useParams, useNavigate, Link, useLocation } from "react-router-dom";
|
||||
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
@@ -30,7 +30,9 @@ import {
|
||||
Edit as EditIcon,
|
||||
KeyboardArrowRight as KeyboardArrowRightIcon,
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
FileCopy as FileCopyIcon
|
||||
FileCopy as FileCopyIcon,
|
||||
ChevronLeft,
|
||||
ChevronRight
|
||||
} from "@mui/icons-material";
|
||||
import { fontGrid } from "@mui/material/styles/cssUtils.js";
|
||||
|
||||
@@ -51,6 +53,7 @@ const dividerColor = "rgb(225, 228, 232)";
|
||||
const hrefStyle = {
|
||||
color: "rgba(255, 255, 255, 0.8)",
|
||||
textDecoration: "none",
|
||||
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
|
||||
};
|
||||
|
||||
|
||||
@@ -152,11 +155,33 @@ export const OuterLink = (props) => {
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const Img = (props) => {
|
||||
// Find parent container and check width
|
||||
|
||||
const isArticlePage = window.location.pathname.includes("/articles/");
|
||||
var height = "auto"
|
||||
var width = 750
|
||||
var width = isArticlePage ? 1000 : 750
|
||||
|
||||
const docsImageStyle = {
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
width: width,
|
||||
maxWidth: width,
|
||||
margin: "auto",
|
||||
marginTop: 10,
|
||||
marginBottom: 10
|
||||
}
|
||||
|
||||
const articleImageStyle = {
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
minWidth: 350,
|
||||
maxWidth: "100%",
|
||||
textAlign: "center",
|
||||
margin: "auto",
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
}
|
||||
|
||||
if (props.height !== undefined && props.height !== null) {
|
||||
height = props.height
|
||||
}
|
||||
@@ -166,11 +191,13 @@ export const Img = (props) => {
|
||||
}
|
||||
|
||||
return(
|
||||
<img
|
||||
style={{border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette?.borderRadius, width: width, maxWidth: width, margin: "auto", marginTop: 10, marginBottom: 10, }}
|
||||
alt={props.alt}
|
||||
src={props.src}
|
||||
/>
|
||||
<div style={{width: "100%", textAlign: "center", }}>
|
||||
<img
|
||||
style={isArticlePage ? articleImageStyle : docsImageStyle}
|
||||
alt={props.alt}
|
||||
src={props.src}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -251,8 +278,10 @@ export const CodeHandler = (props) => {
|
||||
}
|
||||
|
||||
const Docs = (defaultprops) => {
|
||||
const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded } = defaultprops;
|
||||
const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded, userdata } = defaultprops;
|
||||
|
||||
let navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
// Quickfix for react router 5 -> 6
|
||||
const params = useParams();
|
||||
//var props = JSON.parse(JSON.stringify(defaultprops))
|
||||
@@ -283,15 +312,31 @@ const Docs = (defaultprops) => {
|
||||
serverside === true ? "" : window.location.href
|
||||
);
|
||||
const [hashRendered, setHashRendered] = React.useState(false)
|
||||
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [activeSubItem, setActiveSubItem] = useState(false);
|
||||
const headingElementsRef = useRef({})
|
||||
var isArticlePage = window.location.pathname.includes("/articles/") || window.location.pathname === "/articles" ? true : false;
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
//if (params["key"] === undefined) {
|
||||
// navigate("/docs/about")
|
||||
// return
|
||||
//}
|
||||
}, [])
|
||||
fetchDocList();
|
||||
setSidebarOpen(false);
|
||||
|
||||
if (props.match.params === undefined || props.match.params === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const propkey = props.match.params.key
|
||||
if (propkey === undefined || propkey === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (location.pathname.includes("/docs/")) {
|
||||
if (propkey === "cookie_policy" || propkey === "compliance" || propkey === "privacy_policy" || propkey === "terms_of_service") {
|
||||
navigate(`/legal/${propkey}`)
|
||||
}
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
@@ -460,8 +505,13 @@ const Docs = (defaultprops) => {
|
||||
}
|
||||
|
||||
const SidebarPaperStyle = {
|
||||
backgroundColor: "rgb(26,26,26)",
|
||||
backgroundColor: isArticlePage ? "transparent" : "rgb(26,26,26)",
|
||||
border: isArticlePage ? "none" : undefined,
|
||||
borderRadius: isArticlePage ? "none" : undefined,
|
||||
boxShadow: isArticlePage ? "none" : undefined,
|
||||
backgroundImage: "none",
|
||||
width: isArticlePage ? "100%" : undefined,
|
||||
height: isArticlePage ? "100%" : undefined,
|
||||
overflowX: "hidden",
|
||||
position: "relative",
|
||||
paddingLeft: 15,
|
||||
@@ -527,7 +577,7 @@ const Docs = (defaultprops) => {
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
padding: 15,
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
marginBottom: 25,
|
||||
marginBottom: isArticlePage ? 25 : 30,
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
@@ -620,7 +670,7 @@ const Docs = (defaultprops) => {
|
||||
style={{
|
||||
width: "90%",
|
||||
marginTop: 60,
|
||||
marginBottom: 20,
|
||||
marginBottom: isArticlePage ? 40 : 20,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
}}
|
||||
/>
|
||||
@@ -630,7 +680,21 @@ const Docs = (defaultprops) => {
|
||||
marginBlock: "0.85em", alignItems: "center"
|
||||
}}>
|
||||
{element}
|
||||
<Link to={`#${id}`} style={{
|
||||
<Link to={`#${id}`}
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent default navigation
|
||||
document.getElementById(id)?.scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
const url = `${window.location.origin}${window.location.pathname}#${id}`;
|
||||
navigator.clipboard.writeText(url);
|
||||
toast("Link copied to clipboard", {
|
||||
position: "bottom-center",
|
||||
autoClose: 2000,
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
textDecoration: "none", color: "white",
|
||||
paddingLeft: "0.3em", rotate: "-30deg",
|
||||
paddingTop: "0.9em", display: props.level === 1 ? "none" : "block",
|
||||
@@ -638,8 +702,7 @@ const Docs = (defaultprops) => {
|
||||
<LinkIcon />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{extraInfo}
|
||||
{isArticlePage ? (userdata?.support ? extraInfo : "") : extraInfo}
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
@@ -647,21 +710,56 @@ const Docs = (defaultprops) => {
|
||||
|
||||
const SideBar = {
|
||||
width: "17%",
|
||||
borderRight: !isArticlePage ? "1px solid rgba(255,255,255,0.3)" : undefined,
|
||||
minWidth: isArticlePage ? "250px" : "17%",
|
||||
maxWidth: isArticlePage ? "250px" : "17%",
|
||||
marginLeft: isArticlePage ? sidebarOpen ? 40 : 50 : undefined,
|
||||
position: "sticky",
|
||||
top: 50,
|
||||
paddingTop: "0.25em",
|
||||
minHeight: "95vh",
|
||||
maxHeight: "95vh",
|
||||
top: isArticlePage ? 120 : 50,
|
||||
paddingTop: isArticlePage ? "0.65em" : "0.25em",
|
||||
paddingRight: isArticlePage ? "1em" : undefined,
|
||||
minHeight: isArticlePage ? "80vh" : "95vh",
|
||||
maxHeight: isArticlePage ? "80vh" : "95vh",
|
||||
overflowX: "hidden",
|
||||
overflowY: "auto",
|
||||
zIndex: 1000,
|
||||
//borderRight: "1px solid rgba(255,255,255,0.3)",
|
||||
backgroundColor: isArticlePage ? "#212121" : undefined,
|
||||
borderRadius: isArticlePage ? theme.palette?.borderRadius : undefined,
|
||||
transition: isArticlePage ? "width 0.3s ease, min-width 0.3s ease" : undefined,
|
||||
};
|
||||
|
||||
const sideBarToggleButton = {
|
||||
position: "absolute",
|
||||
bottom: sidebarOpen ? 20 : "50%",
|
||||
right: sidebarOpen ? 30 : 21,
|
||||
zIndex: 1000,
|
||||
backgroundColor: "#212121",
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
borderRadius: "50%",
|
||||
width: 40,
|
||||
height: 40,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer",
|
||||
transition: "transform 0.3s ease",
|
||||
"&:hover": {
|
||||
backgroundColor: "#2c2c2c",
|
||||
}
|
||||
};
|
||||
|
||||
const collapsedSideBar = {
|
||||
...SideBar,
|
||||
width: "40px",
|
||||
minWidth: "40px",
|
||||
maxWidth: "40px",
|
||||
overflow: "hidden",
|
||||
};
|
||||
|
||||
const IndexBar = {
|
||||
alignSelf: "flex-start",
|
||||
position: "sticky",
|
||||
top: 80,
|
||||
top: 120,
|
||||
overflowY: "auto",
|
||||
minHeight: "93vh",
|
||||
maxHeight: "93vh",
|
||||
@@ -670,8 +768,15 @@ const Docs = (defaultprops) => {
|
||||
overflow: "hidden",
|
||||
}
|
||||
|
||||
const fetchDocList = () => {
|
||||
fetch(`${globalUrl}/api/v1/docs`, {
|
||||
const fetchDocList = (resetCache = false) => {
|
||||
var url = `${globalUrl}/api/v1/docs`
|
||||
if (location.pathname.includes("/legal")) {
|
||||
url = `${globalUrl}/api/v1/docs?folder=legal&resetCache=${resetCache}`
|
||||
} else if (location.pathname.includes("/articles")) {
|
||||
url = `${globalUrl}/api/v1/docs?folder=articles&resetCache=${resetCache}`
|
||||
}
|
||||
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -691,8 +796,19 @@ const Docs = (defaultprops) => {
|
||||
.catch((error) => { });
|
||||
};
|
||||
|
||||
const fetchDocs = (docId) => {
|
||||
fetch(`${globalUrl}/api/v1/docs/${docId}`, {
|
||||
const fetchDoc = (docId) => {
|
||||
if (docId === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
var url = `${globalUrl}/api/v1/docs/${docId}`
|
||||
if (location.pathname.includes("/legal")) {
|
||||
url = `${globalUrl}/api/v1/docs/${docId}?folder=legal`
|
||||
} else if (location.pathname.includes("/articles")) {
|
||||
url = `${globalUrl}/api/v1/docs/${docId}?folder=articles`
|
||||
}
|
||||
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -706,18 +822,32 @@ const Docs = (defaultprops) => {
|
||||
}
|
||||
|
||||
if (responseJson.success && responseJson.reason !== undefined) {
|
||||
|
||||
if(isArticlePage && window.location.pathname.includes("/articles/")) {
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.includes("404: Not Found")) {
|
||||
setData("# Error\nThis page doesn't exist.");
|
||||
toast("This page doesn't exist. Redirecting to the latest article...", {
|
||||
position: "bottom-center",
|
||||
autoClose: 2000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
navigate(`/articles/2.0_release`)
|
||||
}, 2000)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Find <img> tags and translate them into ![]() format
|
||||
const imgRegex = /<img.*?src="(.*?)"/g;
|
||||
const tocRegex = /^## Table of contents[\s\S]*?(?=^## )|^## Table of contents[\s\S]*$/gm;
|
||||
const newdata = responseJson.reason.replace(imgRegex, '').replace(tocRegex, "");
|
||||
setData(newdata);
|
||||
if (docId === undefined) {
|
||||
document.title = "Shuffle automation documentation";
|
||||
document.title = isArticlePage ? "Shuffle Articles introduction" : "Shuffle documentation";
|
||||
} else {
|
||||
document.title = "Shuffle " + docId.replace("_", " ") + " documentation";
|
||||
document.title = "Shuffle " + docId.replace("_", " ") + (isArticlePage ? " articles" : " documentation");
|
||||
}
|
||||
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.includes("404: Not Found")) {
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.includes("404: Not Found") && !isArticlePage) {
|
||||
navigate("/docs")
|
||||
return
|
||||
}
|
||||
@@ -747,6 +877,11 @@ const Docs = (defaultprops) => {
|
||||
.catch((error) => { });
|
||||
};
|
||||
|
||||
const handleResetCache = () => {
|
||||
fetchDocList(true);
|
||||
toast("Cache has been reset");
|
||||
}
|
||||
|
||||
if (firstrequest) {
|
||||
setFirstrequest(false);
|
||||
if (!serverside) {
|
||||
@@ -769,11 +904,29 @@ const Docs = (defaultprops) => {
|
||||
// return null
|
||||
//}
|
||||
//
|
||||
if (props.match.params.key === undefined) {
|
||||
|
||||
if (props.match.params.key === undefined && isArticlePage) {
|
||||
fetch(`${globalUrl}/api/v1/articles`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success && responseJson.list && responseJson.list.length > 0) {
|
||||
// Navigate to the latest article
|
||||
if (responseJson?.list[0]?.name) {
|
||||
navigate(`/articles/${responseJson?.list[0].name}`);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error fetching articles:", error);
|
||||
});
|
||||
} else {
|
||||
console.log("DOCID: ", props.match.params.key)
|
||||
fetchDocs(props.match.params.key)
|
||||
fetchDoc(props.match.params.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -782,7 +935,7 @@ const Docs = (defaultprops) => {
|
||||
// Handles search-based changes that origin from outside this file
|
||||
if (serverside !== true && window.location.href !== baseUrl) {
|
||||
setBaseUrl(window.location.href);
|
||||
fetchDocs(props.match.params.key);
|
||||
fetchDoc(props.match.params.key);
|
||||
}
|
||||
|
||||
const markdownStyle = {
|
||||
@@ -793,7 +946,7 @@ const Docs = (defaultprops) => {
|
||||
maxWidth: "100%",
|
||||
minWidth: "100%",
|
||||
overflow: "hidden",
|
||||
fontSize: isMobile ? "1.3rem" : "1rem",
|
||||
fontSize: isMobile ? "1.3rem" : "1.1rem",
|
||||
};
|
||||
|
||||
const alertNote = {
|
||||
@@ -940,20 +1093,51 @@ const Docs = (defaultprops) => {
|
||||
}
|
||||
|
||||
|
||||
const activeHrefStyleToc2 = {
|
||||
...hrefStyleToc2,
|
||||
color: "#f86a3e",
|
||||
};
|
||||
|
||||
const activeListItemStyle = {
|
||||
backgroundColor: "rgba(248, 106, 62, 0.08)", // Slight orange tint
|
||||
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
|
||||
borderLeft: "3px solid #f86a3e",
|
||||
paddingLeft: "13px", // Compensate for the border
|
||||
};
|
||||
|
||||
const toggleSidebar = () => {
|
||||
setSidebarOpen(!sidebarOpen);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// PostDataBrowser Section
|
||||
const postDataBrowser =
|
||||
list === undefined || list === null ? null : (
|
||||
<div style={Body}>
|
||||
<div style={SideBar}>
|
||||
<div style={!isArticlePage ? SideBar : (sidebarOpen ? SideBar : collapsedSideBar)}>
|
||||
<Paper style={SidebarPaperStyle}>
|
||||
<List style={{ listStyle: "none", paddingLeft: "0" }}>
|
||||
<List style={{
|
||||
listStyle: "none",
|
||||
paddingLeft: "0",
|
||||
display: isArticlePage ? sidebarOpen ? "block" : "none" : undefined,
|
||||
opacity: isArticlePage ? sidebarOpen ? 1 : 0 : undefined,
|
||||
transition: isArticlePage ? "opacity 0.3s ease" : undefined,
|
||||
height: isArticlePage ? "90%" : undefined,
|
||||
}}>
|
||||
{list.map((data, index) => {
|
||||
const item = data.name;
|
||||
if (item === undefined) {
|
||||
return null;
|
||||
}
|
||||
const path = "/docs/" + item;
|
||||
|
||||
var path = "/docs/" + item;
|
||||
if (location.pathname.includes("/legal")) {
|
||||
path = "/legal/" + item
|
||||
} else if (location.pathname.includes("/articles")) {
|
||||
path = "/articles/" + item
|
||||
}
|
||||
|
||||
const newname =
|
||||
item.charAt(0).toUpperCase() +
|
||||
item.substring(1).split("_").join(" ").split("-").join(" ");
|
||||
@@ -965,23 +1149,62 @@ const Docs = (defaultprops) => {
|
||||
<ListItemButton
|
||||
component={Link}
|
||||
key={index}
|
||||
style={hrefStyle}
|
||||
style={{
|
||||
...hrefStyle,
|
||||
...(itemMatching ? activeListItemStyle : {}),
|
||||
}}
|
||||
to={path}
|
||||
>
|
||||
<ListItemText
|
||||
style={{ color: itemMatching ? "#f86a3e" : "inherit" }}
|
||||
variant="body1"
|
||||
>
|
||||
{newname}
|
||||
</ListItemText>
|
||||
style={{
|
||||
color: itemMatching ? "#f86a3e" : "inherit",
|
||||
flex: 1,
|
||||
}}
|
||||
primary={
|
||||
<Typography
|
||||
variant="body1"
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{newname}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
<KeyboardArrowRightIcon
|
||||
style={{
|
||||
color: itemMatching ? "#f86a3e" : "inherit",
|
||||
fontSize: "1.1rem",
|
||||
marginRight: "-6px",
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
{
|
||||
isArticlePage && (
|
||||
<IconButton
|
||||
onClick={toggleSidebar}
|
||||
style={sideBarToggleButton}
|
||||
>
|
||||
{sidebarOpen ? <ChevronLeft /> : <ChevronRight />}
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
</Paper>
|
||||
</div>
|
||||
<div style={{ width: "60%", margin: "30px 0px 30px 30px", overflow: "hidden", paddingRight: 50, paddingLeft: 40 }}>
|
||||
<div style={{
|
||||
width: isArticlePage ? sidebarOpen ? "60%" : "70%" : "60%",
|
||||
margin: isArticlePage ? sidebarOpen ? "0 0px 30px 30px" : "0 0px 30px 60px" : "30px 0px 30px 30px",
|
||||
overflow: "hidden",
|
||||
paddingRight: 50,
|
||||
paddingLeft: 40,
|
||||
transition: "width 0.3s ease, margin 0.3s ease",
|
||||
}}>
|
||||
{props.match.params.key === undefined ?
|
||||
mainpageInfo
|
||||
:
|
||||
@@ -1003,12 +1226,26 @@ const Docs = (defaultprops) => {
|
||||
}
|
||||
</div>
|
||||
<div style={IndexBar}>
|
||||
{userdata?.support && isArticlePage && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={handleResetCache}
|
||||
style={{marginBottom: "15px"}}
|
||||
>
|
||||
Reset Cache
|
||||
</Button>
|
||||
)}
|
||||
{tocLines.length > 0 ?
|
||||
(
|
||||
<h4 style={{ fontWeight: 600, margin: 0, fontSize: "16px", marginBottom: "8px" }}>Table of Content</h4>
|
||||
<h4 style={{ fontWeight: 600, margin: 0, fontSize: "16px", marginBottom: "8px" }}>Table Of Content</h4>
|
||||
|
||||
) : null}
|
||||
<nav>
|
||||
<div
|
||||
style={{
|
||||
overflowY: "auto",
|
||||
maxHeight: "90vh",
|
||||
}}>
|
||||
{tocLines.map((data, index) => {
|
||||
return (
|
||||
<div className="toc"
|
||||
@@ -1019,12 +1256,15 @@ const Docs = (defaultprops) => {
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: 400,
|
||||
padding: "4px 8px 4px 8px",
|
||||
padding: isArticlePage ? "4px 0" : "4px 8px 4px 8px",
|
||||
paddingLeft: isArticlePage ? "0" : "8px",
|
||||
paddingRight: isArticlePage ? "0" : "8px",
|
||||
lineHeight: "20px",
|
||||
color: activeId === data.id ? "#f86a3e" : "rgba(255,255,255,0.6)",
|
||||
color: activeId === data.id ? "#f86a3e" : "inherit",
|
||||
}}
|
||||
onClick={(e) => {
|
||||
handleCollapse(index)
|
||||
setActiveId(data.id)
|
||||
}}
|
||||
>
|
||||
{data.title}
|
||||
@@ -1041,8 +1281,12 @@ const Docs = (defaultprops) => {
|
||||
return (
|
||||
<ListItemButton
|
||||
key={i}
|
||||
style={hrefStyleToc2}
|
||||
style={activeSubItem === d.id ? activeHrefStyleToc2 : hrefStyleToc2}
|
||||
href={`#${d.id}`}
|
||||
onClick={(e) => {
|
||||
// e.preventDefault()
|
||||
setActiveSubItem(d.id)
|
||||
}}
|
||||
>
|
||||
{d.title}
|
||||
</ListItemButton>
|
||||
@@ -1055,7 +1299,7 @@ const Docs = (defaultprops) => {
|
||||
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
</div >
|
||||
|
||||
</div >
|
||||
@@ -1083,7 +1327,7 @@ const Docs = (defaultprops) => {
|
||||
color="primary"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div style={{ color: "white" }}>More docs</div>
|
||||
<div style={{ color: "white" }}>More {isArticlePage ? "articles" : "docs"}</div>
|
||||
</Button>
|
||||
<Menu
|
||||
id="simple-menu"
|
||||
@@ -1099,7 +1343,7 @@ const Docs = (defaultprops) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const path = "/docs/" + item;
|
||||
const path = isArticlePage ? "/articles/" + item : "/docs/" + item;
|
||||
const newname =
|
||||
item.charAt(0).toUpperCase() +
|
||||
item.substring(1).split("_").join(" ").split("-").join(" ");
|
||||
@@ -1108,7 +1352,8 @@ const Docs = (defaultprops) => {
|
||||
key={index}
|
||||
style={{ color: "white" }}
|
||||
onClick={() => {
|
||||
window.location.pathname = path;
|
||||
navigate(path)
|
||||
handleClose()
|
||||
}}
|
||||
>
|
||||
{newname}
|
||||
@@ -1140,8 +1385,22 @@ const Docs = (defaultprops) => {
|
||||
<MenuItem
|
||||
key={index}
|
||||
style={{ color: "white" }}
|
||||
component={Link}
|
||||
to={`#${data.id}`}
|
||||
onClick={() => {
|
||||
console.log(`Scrolling to: ${data.id}`);
|
||||
setTimeout(() => {
|
||||
const element = document.getElementById(data?.id);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
} else {
|
||||
console.error(`Element with id ${data.id} not found.`);
|
||||
}
|
||||
}, 100); // Delay to ensure the content is loaded
|
||||
handleCloseToc();
|
||||
}}
|
||||
>
|
||||
<a href={`#${data.id}`} style={hrefStyle}>{data.title}</a>
|
||||
{data.title}
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
@@ -1178,7 +1437,7 @@ const Docs = (defaultprops) => {
|
||||
color="primary"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div style={{ color: "white" }}>More docs</div>
|
||||
<div style={{ color: "white" }}>More {isArticlePage ? "articles" : "docs"}</div>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
@@ -1198,7 +1457,7 @@ export default Docs;
|
||||
|
||||
const DocsContent = memo(({postDataBrowser, postDataMobile}) => {
|
||||
return(
|
||||
<div>
|
||||
<div style={{fontFamily: theme?.palette?.fontFamily}}>
|
||||
<BrowserView>{postDataBrowser}</BrowserView>
|
||||
<MobileView>{postDataMobile}</MobileView>
|
||||
</div>
|
||||
@@ -1210,12 +1469,14 @@ const DocsWrapper = memo(({isLoggedIn, isLoaded, children })=>{
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
marginTop: window.location.pathname.includes("/articles/") ? 50 : undefined,
|
||||
paddingBottom: 50,
|
||||
minHeight: 1000, zIndex: 1,
|
||||
maxWidth: Math.min(!(isLoggedIn && isLoaded) ? 1920 : leftSideBarOpenByClick ? windowWidth - 300 : windowWidth - 200, 1920),
|
||||
minWidth: isMobile ? null : (isLoggedIn && isLoaded) ? leftSideBarOpenByClick ? 800 : 900 : null, margin: "auto",
|
||||
position: (isLoggedIn && isLoaded) && leftSideBarOpenByClick ? "relative" : "static",
|
||||
left: (isLoggedIn && isLoaded) && leftSideBarOpenByClick ? 120 : (isLoggedIn && isLoaded) && !leftSideBarOpenByClick ? 80 : 0,
|
||||
marginLeft: windowWidth < 1920 ? leftSideBarOpenByClick && (isLoggedIn && isLoaded) ? 90 : (isLoggedIn && isLoaded) && !leftSideBarOpenByClick ? 80 : 0 : "auto", width: "100%",
|
||||
marginLeft: windowWidth < 1920 ? leftSideBarOpenByClick && (isLoggedIn && isLoaded) ? window.location.pathname.includes("/articles/") ? 0 : 90 : (isLoggedIn && isLoaded) && !leftSideBarOpenByClick ? 80 : 0 : "auto", width: "100%",
|
||||
transition: "left 0.3s ease-in-out, min-width 0.3s ease-in-out, max-width 0.3s ease-in-out, position 0.3s ease-in-out, margin 0.3s ease-in-out, margin-left 0.3s ease"
|
||||
}}>
|
||||
{children}
|
||||
|
||||
@@ -0,0 +1,548 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { useInterval } from "react-powerhooks";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import {
|
||||
CircularProgress,
|
||||
TextField,
|
||||
Button,
|
||||
Paper,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const hrefStyle = {
|
||||
color: "white",
|
||||
textDecoration: "none",
|
||||
};
|
||||
|
||||
const bodyDivStyle = {
|
||||
margin: "auto",
|
||||
marginTop: 150,
|
||||
width: "500px",
|
||||
};
|
||||
|
||||
const useStyles = makeStyles({
|
||||
notchedOutline: {
|
||||
borderColor: "#f85a3e !important",
|
||||
},
|
||||
});
|
||||
|
||||
const LoginDialog = (props) => {
|
||||
const {
|
||||
globalUrl,
|
||||
isLoaded,
|
||||
isLoggedIn,
|
||||
setIsLoggedIn,
|
||||
setCookie,
|
||||
register,
|
||||
checkLogin,
|
||||
} = props;
|
||||
|
||||
let navigate = useNavigate();
|
||||
const classes = useStyles();
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [firstRequest, setFirstRequest] = useState(true);
|
||||
const [loginLoading, setLoginLoading] = useState(false);
|
||||
const [loginViewLoading, setLoginViewLoading] = useState(false);
|
||||
const [ssoUrl, setSSOUrl] = useState("");
|
||||
|
||||
const [MFAField, setMFAField] = useState(false);
|
||||
const [MFAValue, setMFAValue] = useState("");
|
||||
|
||||
|
||||
// Used to swap from login to register. True = login, false = register
|
||||
|
||||
useEffect(() => {
|
||||
checkAdmin()
|
||||
}, [loginViewLoading])
|
||||
|
||||
// Error messages etc
|
||||
const [loginInfo, setLoginInfo] = useState("");
|
||||
|
||||
const handleValidateForm = () => {
|
||||
return username.length > 1 && password.length > 1;
|
||||
};
|
||||
|
||||
if (isLoggedIn === true) {
|
||||
//window.location.pathname = "/workflows";
|
||||
navigate("/workflows")
|
||||
}
|
||||
|
||||
const checkAdmin = () => {
|
||||
const url = globalUrl + "/api/v1/checkusers";
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
|
||||
if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) {
|
||||
setSSOUrl(responseJson.sso_url);
|
||||
}
|
||||
|
||||
navigate("/login")
|
||||
|
||||
if (loginViewLoading) {
|
||||
setLoginViewLoading(false);
|
||||
checkLogin();
|
||||
stop();
|
||||
|
||||
if (
|
||||
responseJson.reason !== undefined &&
|
||||
responseJson.reason !== null
|
||||
) {
|
||||
setLoginInfo(responseJson.reason);
|
||||
}
|
||||
}
|
||||
|
||||
if (responseJson.reason === "stay") {
|
||||
navigate("/adminsetup")
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
if (!loginViewLoading) {
|
||||
setLoginViewLoading(true);
|
||||
start();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const { start, stop } = useInterval({
|
||||
duration: 3000,
|
||||
startImmediate: false,
|
||||
callback: () => {
|
||||
checkAdmin();
|
||||
},
|
||||
})
|
||||
|
||||
if (firstRequest) {
|
||||
setFirstRequest(false);
|
||||
checkAdmin();
|
||||
}
|
||||
|
||||
const onSubmit = (e) => {
|
||||
setLoginLoading(true);
|
||||
e.preventDefault();
|
||||
setLoginInfo("");
|
||||
// FIXME - add some check here ROFL
|
||||
|
||||
// Just use this one?
|
||||
var data = { username: username, password: password };
|
||||
if (MFAValue !== undefined && MFAValue !== null && MFAValue.length > 0) {
|
||||
data["mfa_code"] = MFAValue;
|
||||
}
|
||||
|
||||
var baseurl = globalUrl;
|
||||
if (register) {
|
||||
var url = baseurl + "/api/v1/login";
|
||||
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) => {
|
||||
setLoginLoading(false);
|
||||
if (responseJson["success"] === false) {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
if (responseJson["reason"] === "MFA_REDIRECT") {
|
||||
setLoginInfo(
|
||||
"MFA required. Please enter the 6-digit code from your authenticator"
|
||||
);
|
||||
setMFAField(true);
|
||||
return;
|
||||
} else if (responseJson["reason"] === "MFA_SETUP") {
|
||||
window.location.href = `/login/${responseJson.url}/mfa-setup`;
|
||||
return;
|
||||
}
|
||||
|
||||
setLoginInfo("Successful login, rerouting");
|
||||
for (var key in responseJson["cookies"]) {
|
||||
setCookie(
|
||||
responseJson["cookies"][key].key,
|
||||
responseJson["cookies"][key].value,
|
||||
{ path: "/" }
|
||||
);
|
||||
}
|
||||
|
||||
if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) {
|
||||
console.log("RUN Welcome!!")
|
||||
setTimeout(() => {
|
||||
navigate("/welcome?tab=2")
|
||||
},200)
|
||||
// window.location.pathname = ""
|
||||
return
|
||||
}
|
||||
|
||||
const tmpView = new URLSearchParams(window.location.search).get("view")
|
||||
if (tmpView !== undefined && tmpView !== null) {
|
||||
//const newUrl = `/${tmpView}${decodeURIComponent(window.location.search)}`
|
||||
const newUrl = `/${tmpView}`
|
||||
window.location.pathname = newUrl
|
||||
} else {
|
||||
window.location.pathname = "/workflows"
|
||||
}
|
||||
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginLoading(false);
|
||||
setLoginInfo("Error logging in: " + error);
|
||||
});
|
||||
} else {
|
||||
url = baseurl + "/api/v1/users/register";
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((response) =>
|
||||
response.json().then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
setLoginInfo(responseJson["reason"]);
|
||||
} else {
|
||||
setLoginInfo("Successful register!");
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
setLoginInfo("Error in from backend: ", error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeUser = (e) => {
|
||||
setUsername(e.target.value);
|
||||
};
|
||||
|
||||
const onChangePass = (e) => {
|
||||
setPassword(e.target.value);
|
||||
};
|
||||
|
||||
//const onClickRegister = () => {
|
||||
// if (props.location.pathname === "/login") {
|
||||
// window.location.pathname = "/register"
|
||||
// } else {
|
||||
// window.location.pathname = "/login"
|
||||
// }
|
||||
|
||||
// setLoginCheck(!register)
|
||||
//}
|
||||
|
||||
//var loginChange = register ? (<div><p onClick={setLoginCheck(false)}>Want to register? Click here.</p></div>) : (<div><p onClick={setLoginCheck(true)}>Go back to login? Click here.</p></div>);
|
||||
var formtitle = register ? <div>Login</div> : <div>Register</div>;
|
||||
const imgsize = 100;
|
||||
const basedata = (
|
||||
<div style={bodyDivStyle}>
|
||||
<Paper
|
||||
style={{
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
position: "relative",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: -imgsize / 2 - 10,
|
||||
left: 250 - imgsize / 2,
|
||||
height: imgsize,
|
||||
width: imgsize,
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="images/Shuffle_logo.png"
|
||||
style={{
|
||||
height: imgsize + 10,
|
||||
width: imgsize + 10,
|
||||
border: "2px solid rgba(255,255,255,0.6)",
|
||||
borderRadius: imgsize,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{loginViewLoading ? (
|
||||
<div style={{ textAlign: "center", marginTop: 50 }}>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
Waiting for the Shuffle database to become available. This may
|
||||
take up to two minutes.
|
||||
</Typography>
|
||||
|
||||
{loginInfo === undefined ||
|
||||
loginInfo === null ||
|
||||
loginInfo.length === 0 ? null : (
|
||||
<div style={{ marginTop: "10px" }}>Database Response: {loginInfo}</div>
|
||||
)}
|
||||
<CircularProgress color="secondary" style={{ color: "white" }} />
|
||||
|
||||
<Paper
|
||||
style={{
|
||||
paddingLeft: "30px",
|
||||
paddingRight: "30px",
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
position: "relative",
|
||||
textAlign: "left",
|
||||
marginTop: 15,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>
|
||||
Are you sure Shuffle is{" "}
|
||||
<a
|
||||
rel="norefferer"
|
||||
target="_blank"
|
||||
href="https://github.com/frikky/Shuffle/blob/master/.github/install-guide.md"
|
||||
style={{ textDecoration: "none", color: "#f86a3e" }}
|
||||
>
|
||||
installed correctly
|
||||
</a>
|
||||
?
|
||||
</b>
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>1.</b> Make sure shuffle-database folder has correct access, and that you have a minimum of <b>2Gb of RAM available</b>:{" "}
|
||||
<br />
|
||||
<br />
|
||||
sudo chown -R 1000:1000 shuffle-database
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>2.</b> Disable memory swap on the host:
|
||||
<br />
|
||||
<br />
|
||||
sudo swapoff -a
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>3</b>. Restart the database:
|
||||
<br />
|
||||
<br />
|
||||
sudo docker restart shuffle-opensearch
|
||||
</Typography>
|
||||
</Paper>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 10, color: "white", marginTop: 20 }}
|
||||
>
|
||||
Need help?{" "}
|
||||
<a
|
||||
rel="norefferer"
|
||||
target="_blank"
|
||||
href="https://discord.gg/B2CBzUm"
|
||||
style={{ textDecoration: "none", color: "#f86a3e" }}
|
||||
>
|
||||
Join the Discord!
|
||||
</a>
|
||||
</Typography>
|
||||
</div>
|
||||
) : (
|
||||
<form
|
||||
onSubmit={onSubmit}
|
||||
style={{ margin: "15px 15px 15px 15px", color: "white" }}
|
||||
>
|
||||
<h2>{formtitle}</h2>
|
||||
Username
|
||||
<div>
|
||||
<TextField
|
||||
color="primary"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
marginTop: 5,
|
||||
}}
|
||||
autoFocus
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
height: "50px",
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
required
|
||||
fullWidth={true}
|
||||
autoComplete="username"
|
||||
placeholder="username@example.com"
|
||||
id="emailfield"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangeUser}
|
||||
/>
|
||||
</div>
|
||||
Password
|
||||
<div>
|
||||
<TextField
|
||||
color="primary"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
marginTop: 5,
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
height: "50px",
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
required
|
||||
id="outlined-password-input"
|
||||
fullWidth={true}
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
placeholder="**********"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={onChangePass}
|
||||
/>
|
||||
</div>
|
||||
{MFAField === true ? (
|
||||
<div style={{ marginTop: 15 }}>
|
||||
2-factor code
|
||||
<TextField
|
||||
color="primary"
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
marginTop: 5,
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
height: "50px",
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
required
|
||||
id="outlined-password-input"
|
||||
fullWidth={true}
|
||||
type="text"
|
||||
placeholder="6-digit code"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
onChange={(event) => {
|
||||
setMFAValue(event.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div style={{ display: "flex", marginTop: "15px" }}>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
type="submit"
|
||||
style={{ flex: "1" }}
|
||||
disabled={!handleValidateForm() || loginLoading}
|
||||
>
|
||||
{loginLoading ? (
|
||||
<CircularProgress
|
||||
color="secondary"
|
||||
style={{ color: "white" }}
|
||||
/>
|
||||
) : (
|
||||
"SUBMIT"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ marginTop: "10px" }}>{loginInfo}</div>
|
||||
{ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0 ? (
|
||||
<div>
|
||||
<Typography style={{ textAlign: "center" }}>Or</Typography>
|
||||
<div style={{ textAlign: "center", margin: 10 }}>
|
||||
<Button
|
||||
fullWidth
|
||||
id="sso_button"
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
type="button"
|
||||
style={{ flex: "1", marginTop: 5 }}
|
||||
onClick={() => {
|
||||
//console.log("CLICK SSO");
|
||||
window.location.href = ssoUrl
|
||||
//navigate(ssoUrl)
|
||||
}}
|
||||
>
|
||||
Use SSO
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</form>
|
||||
)}
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
|
||||
const loadedCheck = isLoaded ? <div>{basedata}</div> : <div></div>;
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
if (ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0) {
|
||||
//id="sso_button"
|
||||
const ssoBtn = document.getElementById("sso_button");
|
||||
if (ssoBtn !== undefined && ssoBtn !== null) {
|
||||
//console.log("SSO BTN: ", ssoBtn)
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
var tmpView = new URLSearchParams(cursearch).get("autologin");
|
||||
if (tmpView !== undefined && tmpView !== null) {
|
||||
if (tmpView === "true") {
|
||||
console.log("Tmp: ", tmpView)
|
||||
ssoBtn.click()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 200);
|
||||
}, [ssoUrl])
|
||||
|
||||
return <div style={{paddingBottom: 150, }}>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
export default LoginDialog;
|
||||
@@ -1946,7 +1946,10 @@ const Workflows = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for setting workflows :O!");
|
||||
toast("Failed deleting workflow. Do you have access?");
|
||||
|
||||
if (bulk !== true) {
|
||||
toast(`Failed deleting workflow ${id}. Do you have access?`);
|
||||
}
|
||||
} else {
|
||||
if (bulk !== true) {
|
||||
toast(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`)
|
||||
|
||||
@@ -1758,9 +1758,17 @@ const Workflows2 = (props) => {
|
||||
let exportFileDefaultName = data.name + ".json";
|
||||
|
||||
data["owner"] = "";
|
||||
data["updated_by"] = "";
|
||||
data["org"] = [];
|
||||
data["org_id"] = "";
|
||||
data["execution_org"] = {};
|
||||
data["created"] = 0
|
||||
data["due_date"] = 0
|
||||
data["edited"] = 0
|
||||
|
||||
data["validation"] = {};
|
||||
data["suborg_distribution"] = []
|
||||
data["parentorg_workflow"] = ""
|
||||
|
||||
// These are backwards.. True = saved before. Very confuse.
|
||||
data["previously_saved"] = false;
|
||||
@@ -2035,7 +2043,10 @@ const Workflows2 = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for setting workflows :O!");
|
||||
toast("Failed deleting workflow. Do you have access?");
|
||||
|
||||
if (bulk !== true) {
|
||||
toast("Failed deleting workflow. Do you have access?");
|
||||
}
|
||||
} else {
|
||||
if (bulk !== true) {
|
||||
toast(`Deleted workflow ${id}. Child Workflows in Suborgs were also removed.`)
|
||||
@@ -2813,7 +2824,7 @@ const Workflows2 = (props) => {
|
||||
|
||||
{(data.sharing !== undefined && data.sharing !== null && data.sharing === "form") || (data?.form_control?.input_markdown !== undefined && data?.form_control?.input_markdown !== null && data?.form_control?.input_markdown !== "") && type !== "public" ?
|
||||
<Tooltip title="Edit Form" placement="top">
|
||||
<div style={{ position: "absolute", top: 45, right: 8, }}>
|
||||
<div style={{ position: "absolute", top: 50, right: 8, }}>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
@@ -2830,8 +2841,8 @@ const Workflows2 = (props) => {
|
||||
: null}
|
||||
|
||||
{(data?.validation?.validation_ran === true && data?.validation?.valid === false && data?.validation?.errors?.length > 0 ) ?
|
||||
<Tooltip title={`Explore more than ${data?.validation?.errors?.length} errors. When the last execution finishes without errors AND notifications stop occuring, this icon disappears.`} placement="top">
|
||||
<div style={{ position: "absolute", top: 45, right: 8, }}>
|
||||
<Tooltip title={`Explore more than ${data?.validation?.errors?.length} notifications. When the last execution finishes without errors AND notifications stop occuring, this icon disappears.`} placement="top">
|
||||
<div style={{ position: "absolute", top: 85, right: 8, }}>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
@@ -2850,8 +2861,8 @@ const Workflows2 = (props) => {
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
: null}
|
||||
|
||||
</Grid>
|
||||
</Paper>
|
||||
</div>
|
||||
@@ -3227,6 +3238,7 @@ const Workflows2 = (props) => {
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title="Amount of triggers"
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
module orborus
|
||||
|
||||
go 1.22.0
|
||||
go 1.22.7
|
||||
|
||||
toolchain go1.23.6
|
||||
|
||||
//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared
|
||||
replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared
|
||||
|
||||
require (
|
||||
github.com/docker/docker v27.5.0+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.8.3
|
||||
github.com/shuffle/shuffle-shared v0.8.7
|
||||
k8s.io/api v0.30.2
|
||||
k8s.io/apimachinery v0.30.2
|
||||
)
|
||||
|
||||
@@ -123,12 +123,10 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
|
||||
github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
|
||||
github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
|
||||
github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E=
|
||||
github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
@@ -305,10 +303,11 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shuffle/shuffle-shared v0.6.99 h1:sPGmZo+8JMgUH9Q2O659za2w4sF/NXiWFCrSPm1nTAU=
|
||||
github.com/shuffle/shuffle-shared v0.6.99/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ=
|
||||
github.com/shuffle/shuffle-shared v0.8.7 h1:+UdRx7b/KUy/E92ODNr87UvCI2Dxfnv0samQWuggJu4=
|
||||
github.com/shuffle/shuffle-shared v0.8.7/go.mod h1:bBXhEsPKjxln0mFnSeri7gIJ3tL/636Sh5NyTyNrvIQ=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
|
||||
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
|
||||
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
@@ -369,7 +368,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -427,7 +425,6 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -437,7 +434,6 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -474,14 +470,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
@@ -456,6 +456,7 @@ func deployServiceWorkers(image string) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
isMemcachedRunning, err := checkMemcached(ctx, dockercli)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed checking memcached: %s", err)
|
||||
@@ -466,8 +467,10 @@ func deployServiceWorkers(image string) {
|
||||
}
|
||||
|
||||
ip := "shuffle-cache"
|
||||
|
||||
os.Setenv("SHUFFLE_MEMCACHED", fmt.Sprintf("%s:11211", ip))
|
||||
if len(os.Getenv("SHUFFLE_MEMCACHED")) == 0 {
|
||||
os.Setenv("SHUFFLE_MEMCACHED", fmt.Sprintf("%s:11211", ip))
|
||||
}
|
||||
*/
|
||||
|
||||
defaultNetworkAttach := false
|
||||
if containerId != "" {
|
||||
@@ -608,7 +611,7 @@ func deployServiceWorkers(image string) {
|
||||
fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", os.Getenv("SHUFFLE_LOGS_DISABLED")),
|
||||
fmt.Sprintf("DEBUG_MEMORY=%s", os.Getenv("DEBUG_MEMORY")),
|
||||
fmt.Sprintf("SHUFFLE_APP_SDK_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_SDK_TIMEOUT")),
|
||||
fmt.Sprintf("SHUFFLE_MAX_SWARM_NODES=%d", os.Getenv("SHUFFLE_MAX_SWARM_NODES")),
|
||||
fmt.Sprintf("SHUFFLE_MAX_SWARM_NODES=%s", os.Getenv("SHUFFLE_MAX_SWARM_NODES")),
|
||||
fmt.Sprintf("SHUFFLE_BASE_IMAGE_NAME=%s", os.Getenv("SHUFFLE_BASE_IMAGE_NAME")),
|
||||
fmt.Sprintf("SHUFFLE_APP_REQUEST_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_REQUEST_TIMEOUT")),
|
||||
},
|
||||
@@ -766,10 +769,8 @@ func handleBackendImageDownload(ctx context.Context, images string) error {
|
||||
|
||||
// Remove the image
|
||||
handled := []string{}
|
||||
log.Printf("[DEBUG] Should remove existing image (s): %s. Waiting 30 seconds to ensure backend has the latest images built and ready to distribute.", images)
|
||||
removeOptions := image.RemoveOptions{}
|
||||
|
||||
time.Sleep(time.Duration(30) * time.Second)
|
||||
log.Printf("[DEBUG] Removing existing image (s): %s. Waiting 30 seconds before starting to ensure backend has the latest images built and ready to distribute.", images)
|
||||
//time.Sleep(time.Duration(30) * time.Second)
|
||||
|
||||
newImages := []string{}
|
||||
for _, image := range strings.Split(images, ",") {
|
||||
@@ -785,23 +786,12 @@ func handleBackendImageDownload(ctx context.Context, images string) error {
|
||||
|
||||
newImages = append(newImages, image)
|
||||
|
||||
// There is no real point in actual removal. This may however be a good idea, as Worker will force download the new one anyway
|
||||
resp, err := dockercli.ImageRemove(ctx, image, removeOptions)
|
||||
log.Printf("[DEBUG] Downloading image: %s", image)
|
||||
err := shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed removing image: %s. Resp: %#v", err, resp)
|
||||
|
||||
// Goroutining images that don't already exist, as they are most likely not the correct one
|
||||
go shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image)
|
||||
log.Printf("[ERROR] Failed downloading image: %s", err)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Removed image: %s", image)
|
||||
|
||||
err = shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed downloading image: %s", err)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Downloaded image: %s", image)
|
||||
//break
|
||||
}
|
||||
log.Printf("[DEBUG] Downloaded image: %s", image)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,20 +810,18 @@ func handleBackendImageDownload(ctx context.Context, images string) error {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed finding containers: %s", err)
|
||||
log.Printf("[ERROR] Failed finding services: %s", err)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Found %d services", len(services))
|
||||
|
||||
found := false
|
||||
for _, service := range services {
|
||||
|
||||
log.Printf("Imagename: %s", service.Spec.TaskTemplate.ContainerSpec.Image)
|
||||
//log.Printf("Service image: %s", service.Spec.TaskTemplate.ContainerSpec.Image)
|
||||
|
||||
for _, image := range newImages {
|
||||
if !strings.Contains(service.Spec.TaskTemplate.ContainerSpec.Image, image) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Found service for image %#v: %#v", service.Spec.Annotations.Name)
|
||||
log.Printf("[DEBUG] Found service for image: %#v", service.Spec.Annotations.Name)
|
||||
|
||||
// Update the service to run with the new image
|
||||
//docker service update --image username/imagename:latest servicename --force
|
||||
@@ -853,9 +841,20 @@ func handleBackendImageDownload(ctx context.Context, images string) error {
|
||||
|
||||
if !strings.Contains(fmt.Sprintf("%s", resp), "error") {
|
||||
break
|
||||
} else {
|
||||
found = true
|
||||
log.Printf("[ERROR] Failed updating service %s with the new image %s: %s. Resp: %#v", service.Spec.Annotations.Name, image, err, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
log.Printf("[DEBUG] Failed to find service to update for service %s", newImages)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1479,29 +1478,34 @@ func initializeImages() {
|
||||
}
|
||||
|
||||
// check whether they are the same first
|
||||
images := []string{
|
||||
fmt.Sprintf("frikky/shuffle:app_sdk"),
|
||||
fmt.Sprintf("shuffle/shuffle:app_sdk"),
|
||||
fmt.Sprintf("%s/%s/shuffle-app_sdk:%s", baseimageregistry, baseimagename, appSdkVersion),
|
||||
newWorker,
|
||||
}
|
||||
|
||||
pullOptions := image.PullOptions{}
|
||||
for _, image := range images {
|
||||
if isKubernetes == "true" {
|
||||
log.Printf("[DEBUG] Skipping image pull of '%s' because Kubernetes does it in realtime instead", image)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Pulling image %s", image)
|
||||
reader, err := dockercli.ImagePull(ctx, image, pullOptions)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed getting image %s: %s", image, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
io.Copy(os.Stdout, reader)
|
||||
log.Printf("[DEBUG] Successfully downloaded and built %s", image)
|
||||
if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") != "true" {
|
||||
images := []string{
|
||||
fmt.Sprintf("frikky/shuffle:app_sdk"),
|
||||
fmt.Sprintf("shuffle/shuffle:app_sdk"),
|
||||
fmt.Sprintf("%s/%s/shuffle-app_sdk:%s", baseimageregistry, baseimagename, appSdkVersion),
|
||||
newWorker,
|
||||
}
|
||||
|
||||
pullOptions := image.PullOptions{}
|
||||
for _, image := range images {
|
||||
if isKubernetes == "true" {
|
||||
log.Printf("[DEBUG] Skipping image pull of '%s' because Kubernetes does it in realtime instead", image)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Pulling image %s", image)
|
||||
reader, err := dockercli.ImagePull(ctx, image, pullOptions)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed getting image %s: %s", image, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
io.Copy(os.Stdout, reader)
|
||||
log.Printf("[DEBUG] Successfully downloaded and built %s", image)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("[DEBUG] Skipping image download as SHUFFLE_AUTO_IMAGE_DOWNLOAD is set to true")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2245,7 +2249,7 @@ func main() {
|
||||
|
||||
toBeRemoved.Data = append(toBeRemoved.Data, incRequest)
|
||||
} else if incRequest.Type == "DOCKER_IMAGE_DOWNLOAD" {
|
||||
log.Printf("[INFO] Should delete -> download new images: %#v", incRequest.ExecutionArgument)
|
||||
log.Printf("[INFO] Re-downloading new image(s): %#v", incRequest.ExecutionArgument)
|
||||
|
||||
if len(incRequest.ExecutionArgument) > 0 {
|
||||
// FIXME: Wait X seconds before running this as the image build may not be done yet. This is shitty, but may be ok to do in Orborus. Easy fix for the future: Just let it run through jobs 5-10 times before actually picking it up
|
||||
@@ -3462,7 +3466,7 @@ func sendPipelineHealthStatus() (shuffle.LakeConfig, error) {
|
||||
|
||||
} else {
|
||||
tenzirDisabled = true
|
||||
log.Printf("[ERROR] Disabling pipelines: %s. You will need to restart the Orborus to fix this.", err)
|
||||
log.Printf("[WARNING] Disabling pipelines: %s. You will need to restart the Orborus to fix this.", err)
|
||||
}
|
||||
|
||||
return pipelinePayload, err
|
||||
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
github.com/docker/docker v27.5.0+incompatible
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.8.3
|
||||
github.com/shuffle/shuffle-shared v0.8.7
|
||||
k8s.io/api v0.30.2
|
||||
k8s.io/apimachinery v0.30.2
|
||||
k8s.io/client-go v0.30.2
|
||||
|
||||
@@ -106,9 +106,7 @@ var window = shuffle.NewTimeWindow(10 * time.Second)
|
||||
// Images to be autodeployed in the latest version of Shuffle.
|
||||
var autoDeploy = map[string]string{
|
||||
"http:1.4.0": "frikky/shuffle:http_1.4.0",
|
||||
"http:1.3.0": "frikky/shuffle:http_1.3.0",
|
||||
"shuffle-tools:1.2.0": "frikky/shuffle:shuffle-tools_1.2.0",
|
||||
"shuffle-subflow:1.0.0": "frikky/shuffle:shuffle-subflow_1.0.0",
|
||||
"shuffle-subflow:1.1.0": "frikky/shuffle:shuffle-subflow_1.1.0",
|
||||
// "shuffle-tools-fork:1.0.0": "frikky/shuffle:shuffle-tools-fork_1.0.0",
|
||||
}
|
||||
|
||||