@@ -14,17 +14,18 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
architecture: [x64, arm64]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
|
||||
- name: Install Docker Compose
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker-compose
|
||||
docker-compose --version
|
||||
|
||||
- name: Set up opensearch directory
|
||||
run: mkdir shuffle-database
|
||||
continue-on-error: true
|
||||
|
||||
- name: Elevate rights for it
|
||||
run: chmod -R 777 shuffle-database
|
||||
run: chmod -R 777 shuffle-database
|
||||
|
||||
- name: Build the stack
|
||||
run: docker compose up -d
|
||||
@@ -59,7 +60,6 @@ jobs:
|
||||
echo "The word 'Shuffle' was not found in the response."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Register a user and check the status code
|
||||
run: |
|
||||
MAX_RETRIES=30
|
||||
@@ -100,31 +100,36 @@ jobs:
|
||||
echo "User registration failed after $MAX_RETRIES attempts."
|
||||
exit 1
|
||||
|
||||
- name: Run Selenium testing for frontend
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/frontend
|
||||
# write some log to see the current directory
|
||||
chmod +x frontend-testing.sh
|
||||
./frontend-testing.sh
|
||||
|
||||
- name: Get the API key and run a health check
|
||||
id: health_check
|
||||
run: |
|
||||
RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search')
|
||||
echo "Raw Response: $RESPONSE"
|
||||
|
||||
API_KEY=$(echo "$RESPONSE" | jq -r '.hits.hits[0]._source.apikey')
|
||||
if [ -n "$API_KEY" ]; then
|
||||
if [ -n "$API_KEY" ] && [ "$API_KEY" != "null" ]; then
|
||||
echo "Admin API key: $API_KEY"
|
||||
echo "API_KEY=$API_KEY" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Failed to retrieve the API key for the admin user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Waiting 1 minute before sending the health API request..."
|
||||
sleep 60
|
||||
|
||||
echo "Checking health API..."
|
||||
HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \
|
||||
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
|
||||
-H 'Accept-Language: en-US,en;q=0.9' \
|
||||
-H 'Connection: keep-alive' \
|
||||
-H 'Sec-Fetch-Dest: document' \
|
||||
-H 'Sec-Fetch-Mode: navigate' \
|
||||
-H 'Sec-Fetch-Site: none' \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-H 'Sec-Fetch-User: ?1' \
|
||||
-H 'Upgrade-Insecure-Requests: 1' \
|
||||
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
|
||||
-H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
|
||||
-H 'sec-ch-ua-mobile: ?0' \
|
||||
-H 'sec-ch-ua-platform: "macOS"')
|
||||
-H "Authorization: Bearer $API_KEY")
|
||||
|
||||
echo "Health API Response: $HEALTH_RESPONSE"
|
||||
|
||||
WORKFLOWS_CREATE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.create')
|
||||
WORKFLOWS_RUN=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run')
|
||||
@@ -132,9 +137,35 @@ jobs:
|
||||
WORKFLOWS_RUN_STATUS=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_status')
|
||||
WORKFLOWS_DELETE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.delete')
|
||||
|
||||
echo "WORKFLOWS_CREATE: $WORKFLOWS_CREATE"
|
||||
echo "WORKFLOWS_RUN: $WORKFLOWS_RUN"
|
||||
echo "WORKFLOWS_RUN_FINISHED: $WORKFLOWS_RUN_FINISHED"
|
||||
echo "WORKFLOWS_RUN_STATUS: $WORKFLOWS_RUN_STATUS"
|
||||
echo "WORKFLOWS_DELETE: $WORKFLOWS_DELETE"
|
||||
|
||||
if [ "$WORKFLOWS_CREATE" = "true" ] && [ "$WORKFLOWS_RUN" = "true" ] && [ "$WORKFLOWS_RUN_FINISHED" = "true" ] && [ "$WORKFLOWS_RUN_STATUS" = "FINISHED" ] && [ "$WORKFLOWS_DELETE" = "true" ]; then
|
||||
echo "Health endpoint check was successful."
|
||||
exit 0
|
||||
else
|
||||
echo "Health endpoint check failed. Response did not meet expected criteria."
|
||||
echo "Health check failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
notify:
|
||||
needs: build
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Twilio Alert
|
||||
run: |
|
||||
WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
MESSAGE="🚨 Shuffle Workflow Failed!
|
||||
Repository: ${{ github.repository }}
|
||||
Branch: ${{ github.ref_name }}
|
||||
Workflow URL: $WORKFLOW_URL"
|
||||
|
||||
curl -s -X POST https://api.twilio.com/2010-04-01/Accounts/${{ secrets.TWILIO_ACCOUNT_SID }}/Messages.json \
|
||||
--data-urlencode "To=${{ secrets.TWILIO_TO_NUMBER }}" \
|
||||
--data-urlencode "From=${{ secrets.TWILIO_FROM_NUMBER }}" \
|
||||
--data-urlencode "Body=$MESSAGE" \
|
||||
-u "${{ secrets.TWILIO_ACCOUNT_SID }}:${{ secrets.TWILIO_AUTH_TOKEN }}" > /dev/null
|
||||
|
||||
@@ -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}`))
|
||||
}
|
||||
|
||||
+11
-9
@@ -1,7 +1,9 @@
|
||||
module shuffle
|
||||
|
||||
go 1.22.2
|
||||
toolchain go1.24.1
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.7
|
||||
|
||||
//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
|
||||
|
||||
@@ -19,8 +21,8 @@ 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.12
|
||||
golang.org/x/crypto v0.35.0
|
||||
github.com/shuffle/shuffle-shared v0.8.18
|
||||
golang.org/x/crypto v0.36.0
|
||||
google.golang.org/api v0.176.1
|
||||
google.golang.org/grpc v1.68.1
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
@@ -121,12 +123,12 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v0.11.0 // indirect
|
||||
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.36.0 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
|
||||
+5
-14
@@ -347,8 +347,6 @@ 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.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=
|
||||
@@ -423,8 +421,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -480,8 +476,7 @@ 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.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -497,8 +492,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -527,14 +521,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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.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.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -545,8 +537,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
SERVER_URL="http://localhost:3000"
|
||||
|
||||
declare -a Routes=($(grep -oP '(?<=path=")[^"]*' src/App.jsx | grep -E '^[a-zA-Z0-9/_:-]+$' | grep -vE '^/$'))
|
||||
|
||||
for i in "${!Routes[@]}"; do
|
||||
Routes[$i]="${Routes[$i]#/}"
|
||||
done
|
||||
|
||||
# # Add all tab routes and unique routes here
|
||||
Routes+=(
|
||||
'workflows?tab=org_workflows'
|
||||
'workflows?tab=my_workflows'
|
||||
'workflows?tab=all_workflows'
|
||||
'apps/gmail'
|
||||
'apis/gmail'
|
||||
'apps?tab=my_apps'
|
||||
'apps?tab=all_apps'
|
||||
'search?tab=org_apps'
|
||||
'search?tab=my_apps'
|
||||
'search?tab=workflows'
|
||||
'search?tab=docs'
|
||||
'search?tab=creators'
|
||||
'search?tab=discord'
|
||||
"admin?tab=organization"
|
||||
"admin?tab=users"
|
||||
"admin?tab=app_auth"
|
||||
"admin?tab=datastore"
|
||||
"admin?tab=files"
|
||||
"admin?tab=triggers"
|
||||
"admin?tab=locations"
|
||||
"admin?tab=tenants"
|
||||
"admin?admin_tab=org_config"
|
||||
"admin?admin_tab=sso"
|
||||
"admin?admin_tab=notifications"
|
||||
"admin?admin_tab=billingstats"
|
||||
"admin?admin_tab=branding(beta)"
|
||||
)
|
||||
|
||||
# Stop frontend container so it test unpushed changes
|
||||
docker stop shuffle-frontend
|
||||
|
||||
# # Install all frontend dependencies
|
||||
yarn add selenium-webdriver --dev && yarn install
|
||||
echo "Starting frontend..."
|
||||
|
||||
BROWSER=none yarn start &
|
||||
SERVER_PID=$!
|
||||
echo "Frontend started with PID: $SERVER_PID"
|
||||
|
||||
echo "Waiting for 1 minute to ensure the server is fully up..."
|
||||
sleep 60
|
||||
|
||||
echo "Server is up! Starting Selenium tests..."
|
||||
|
||||
echo "Starting frontend tests..."
|
||||
node selenium-test.js "$SERVER_URL" "${Routes[@]}"
|
||||
|
||||
TEST_EXIT_CODE=$?
|
||||
|
||||
if [[ $TEST_EXIT_CODE -ne 0 ]]; then
|
||||
echo "Selenium tests failed. Exiting..."
|
||||
kill $SERVER_PID
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kill $SERVER_PID
|
||||
echo "Testing complete. See above logs for errors if any."
|
||||
|
||||
# Starting shuffle-frontend container
|
||||
echo "Starting shuffle-frontend container..."
|
||||
docker start shuffle-frontend
|
||||
echo "shuffle-frontend started successfully."
|
||||
exit 0
|
||||
@@ -128,6 +128,7 @@
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"postcss": "^8.4.38",
|
||||
"promise-window": "^1.2.1",
|
||||
"selenium-webdriver": "^4.29.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
const { Builder, By, until } = require('selenium-webdriver');
|
||||
const chrome = require('selenium-webdriver/chrome');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
console.log('Starting Selenium script for testing pages...');
|
||||
|
||||
(async () => {
|
||||
const userDataDir = path.join(__dirname, 'chrome-user-data', `${Date.now()}-${Math.random().toString(36).substring(2, 8)}`);
|
||||
fs.mkdirSync(userDataDir, { recursive: true });
|
||||
|
||||
let options = new chrome.Options();
|
||||
options.addArguments('--no-sandbox');
|
||||
options.addArguments('--headless');
|
||||
|
||||
let driver = await new Builder()
|
||||
.forBrowser('chrome')
|
||||
.setChromeOptions(options)
|
||||
.build();
|
||||
|
||||
const SuccessfullyLoadedPath = [];
|
||||
const FailedToLoadPath = [];
|
||||
|
||||
try {
|
||||
await driver.manage().window().setRect({ width: 1600, height: 1200 });
|
||||
await driver.manage().window().maximize()
|
||||
await driver.manage().setTimeouts({ implicit: 15000 });
|
||||
|
||||
const frontendURL = process.argv[2];
|
||||
const routes = process.argv.slice(3);
|
||||
|
||||
console.log('Frontend URL:', frontendURL);
|
||||
const isCloud = frontendURL === 'http://localhost:3002' || frontendURL === 'https://sandbox.shuffler.io' || frontendURL === 'https://shuffler.io';
|
||||
|
||||
if (isCloud) {
|
||||
// Login Credentials
|
||||
const LOGIN_URL = `${frontendURL}/login`;
|
||||
// put your login credentials here
|
||||
const USERNAME = '';
|
||||
const PASSWORD = '';
|
||||
|
||||
console.log('Logging in...');
|
||||
await driver.get(LOGIN_URL);
|
||||
|
||||
try {
|
||||
await driver.wait(until.elementLocated(By.css('#emailfield')), 10000);
|
||||
await driver.findElement(By.css('#emailfield')).sendKeys(USERNAME);
|
||||
|
||||
await driver.wait(until.elementLocated(By.css('#outlined-password-input')), 10000);
|
||||
await driver.findElement(By.css('#outlined-password-input')).sendKeys(PASSWORD);
|
||||
|
||||
await driver.wait(until.elementLocated(By.css('#loginButton')), 10000);
|
||||
await driver.findElement(By.css('#loginButton')).click();
|
||||
|
||||
// Ensure login success by checking URL change
|
||||
await driver.wait(async () => {
|
||||
const url = await driver.getCurrentUrl();
|
||||
return url.includes('welcome');
|
||||
}, 20000);
|
||||
|
||||
console.log('Successfully logged in!');
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error.message);
|
||||
FailedToLoadPath.push(LOGIN_URL);
|
||||
await driver.quit();
|
||||
process.exit(1);
|
||||
}
|
||||
}else {
|
||||
// Steps for onprem testing
|
||||
// 1. Login
|
||||
// 2. Create new workflow
|
||||
|
||||
// Write your own login credentials here if you are testing onprem locally
|
||||
const USERNAME = 'demo@demo.io';
|
||||
const PASSWORD = 'supercoolpassword';
|
||||
const WORKFLOW_URL = `${frontendURL}/workflows`;
|
||||
|
||||
try {
|
||||
// Login
|
||||
console.log('Logging in...');
|
||||
await driver.get(`${frontendURL}/login`);
|
||||
|
||||
await driver.wait(until.elementLocated(By.css('#emailfield')), 10000);
|
||||
await driver.findElement(By.css('#emailfield')).sendKeys(USERNAME);
|
||||
|
||||
await driver.wait(until.elementLocated(By.css('#outlined-password-input')), 10000);
|
||||
await driver.findElement(By.css('#outlined-password-input')).sendKeys(PASSWORD);
|
||||
|
||||
const loginButton = await driver.findElement(By.css('#loginButton'));
|
||||
await driver.executeScript("arguments[0].scrollIntoView(true);", loginButton);
|
||||
await loginButton.click();
|
||||
|
||||
|
||||
|
||||
// Ensure signup success by checking URL change
|
||||
await driver.wait(async () => {
|
||||
const url = await driver.getCurrentUrl();
|
||||
return url.includes('welcome') || url.includes('workflows');
|
||||
}, 20000);
|
||||
|
||||
|
||||
const isParentPresent = await driver.wait(async () => {
|
||||
return await driver.executeScript(
|
||||
"return document.querySelector('.parent-component') !== null;"
|
||||
);
|
||||
}, 5000).catch(() => false);
|
||||
|
||||
if (!isParentPresent) {
|
||||
const logs = await driver.manage().logs().get('browser');
|
||||
const severeErrors = logs.filter(log => log.level.name === 'SEVERE');
|
||||
if (severeErrors.length > 0) {
|
||||
const crashCausingErrors = severeErrors.filter(err => {
|
||||
return !err.message.includes('Warning:') &&
|
||||
!err.message.includes('MUI:')
|
||||
});
|
||||
|
||||
console.error(`Page (${frontendURL}/login) did not load correctly:`, crashCausingErrors);
|
||||
FailedToLoadPath.push(`${frontendURL}/login`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Successfully logged in!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to login:', error.message);
|
||||
FailedToLoadPath.push(WORKFLOW_URL);
|
||||
await driver.quit();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// Create new workflow
|
||||
console.log('Creating new workflow...');
|
||||
await driver.get(WORKFLOW_URL);
|
||||
|
||||
await driver.sleep(1000);
|
||||
|
||||
await driver.wait(until.elementLocated(By.css('#create_workflow_button')), 10000);
|
||||
const create_workflow_button = await driver.findElement(By.css('#create_workflow_button'));
|
||||
|
||||
await driver.sleep(1000);
|
||||
await create_workflow_button.click();
|
||||
|
||||
await driver.sleep(1500);
|
||||
|
||||
await driver.wait(until.elementLocated(By.css('#Enter-Workflow-Name')), 10000);
|
||||
const enter_workflow_name_field = await driver.findElement(By.css('#Enter-Workflow-Name'));
|
||||
|
||||
await driver.sleep(1000);
|
||||
await enter_workflow_name_field.sendKeys("Test Workflow");
|
||||
|
||||
// Wait before saving
|
||||
await driver.sleep(1500);
|
||||
|
||||
await driver.wait(until.elementLocated(By.css('#save_workflow_button')), 10000);
|
||||
const save_workflow_button = await driver.findElement(By.css('#save_workflow_button'));
|
||||
|
||||
await driver.sleep(1000); // Delay before clicking
|
||||
await save_workflow_button.click();
|
||||
|
||||
// Wait for the parent component to appear
|
||||
await driver.sleep(2000);
|
||||
|
||||
const isParentPresent = await driver.wait(async () => {
|
||||
return await driver.executeScript(
|
||||
"return document.querySelector('.parent-component') !== null;"
|
||||
);
|
||||
}, 5000).catch(() => false);
|
||||
|
||||
if (!isParentPresent) {
|
||||
const logs = await driver.manage().logs().get('browser');
|
||||
const severeErrors = logs.filter(log => log.level.name === 'SEVERE');
|
||||
if (severeErrors.length > 0) {
|
||||
const crashCausingErrors = severeErrors.filter(err => {
|
||||
return !err.message.includes('Warning:') &&
|
||||
!err.message.includes('MUI:');
|
||||
});
|
||||
|
||||
console.error(`Page (${WORKFLOW_URL}) did not load correctly:`, crashCausingErrors);
|
||||
FailedToLoadPath.push(`${WORKFLOW_URL}`);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to create new workflow:', error.message);
|
||||
FailedToLoadPath.push(WORKFLOW_URL);
|
||||
await driver.quit();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Get routes from command line arguments
|
||||
if (routes.length === 0) {
|
||||
console.error('No routes found to test.');
|
||||
await driver.quit();
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`Found ${routes.length} routes to test.`);
|
||||
|
||||
for (const route of routes) {
|
||||
const url = `${frontendURL}/${route}`;
|
||||
console.log(`Testing route: ${url}`);
|
||||
|
||||
try {
|
||||
await driver.get(url);
|
||||
await driver.sleep(3000);
|
||||
|
||||
// Wait for document readiness
|
||||
await driver.wait(async () => {
|
||||
return await driver.executeScript('return document.readyState') === 'complete';
|
||||
}, 10000);
|
||||
|
||||
const isParentPresent = await driver.wait(async () => {
|
||||
return await driver.executeScript(
|
||||
"return document.querySelector('.parent-component') !== null;"
|
||||
);
|
||||
}, 5000).catch(() => false);
|
||||
|
||||
if (!isParentPresent) {
|
||||
const logs = await driver.manage().logs().get('browser');
|
||||
const severeErrors = logs.filter(log => log.level.name === 'SEVERE');
|
||||
if (severeErrors.length > 0) {
|
||||
const crashCausingErrors = severeErrors.filter(err => {
|
||||
return !err.message.includes('Warning:') &&
|
||||
!err.message.includes('MUI:')
|
||||
});
|
||||
|
||||
console.error(`Page (${url}) did not load correctly:`, crashCausingErrors);
|
||||
FailedToLoadPath.push(url);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Successfully loaded: ${url}`);
|
||||
SuccessfullyLoadedPath.push(url);
|
||||
} catch (error) {
|
||||
console.error(`Failed to load ${url}:`, error.message);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
console.log("Total pages tested: ", SuccessfullyLoadedPath.length + FailedToLoadPath.length);
|
||||
console.log("Successfully loaded pages: ", SuccessfullyLoadedPath.length);
|
||||
if (FailedToLoadPath.length > 0) {
|
||||
console.log("Failed to load pages: ", FailedToLoadPath.length);
|
||||
console.log("Failed to load pages paths: ", FailedToLoadPath);
|
||||
process.exit(1);
|
||||
}else {
|
||||
console.log("No pages failed to load. Congrats!");
|
||||
}
|
||||
await driver.quit();
|
||||
fs.rmSync(userDataDir, { recursive: true, force: true });
|
||||
}
|
||||
})();
|
||||
+28
-1
@@ -189,6 +189,7 @@ const App = (message, props) => {
|
||||
color: "rgba(255, 255, 255, 0.65)",
|
||||
minHeight: "100vh",
|
||||
}}
|
||||
className='parent-component'
|
||||
>
|
||||
<ScrollToTop
|
||||
getUserNotifications={getUserNotifications}
|
||||
@@ -371,6 +372,7 @@ const App = (message, props) => {
|
||||
element={
|
||||
<Admin
|
||||
isLoggedIn={isLoggedIn}
|
||||
userdata={userdata}
|
||||
setIsLoggedIn={setIsLoggedIn}
|
||||
register={true}
|
||||
isLoaded={isLoaded}
|
||||
@@ -634,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"
|
||||
@@ -651,7 +679,6 @@ const App = (message, props) => {
|
||||
exact
|
||||
path="/docs"
|
||||
element={
|
||||
//navigate(`/docs/about`)
|
||||
<Docs
|
||||
isMobile={isMobile}
|
||||
isLoaded={isLoaded}
|
||||
|
||||
@@ -860,6 +860,7 @@ const Billing = memo((props) => {
|
||||
"Add Card Details"
|
||||
}
|
||||
</Button>
|
||||
|
||||
{userdata.has_card_available === true ?
|
||||
<Button
|
||||
fullWidth
|
||||
@@ -888,7 +889,8 @@ const Billing = memo((props) => {
|
||||
: null}
|
||||
|
||||
</span>
|
||||
: null}
|
||||
: null}
|
||||
|
||||
{showSupport ?
|
||||
<Button variant="outlined" color="primary" style={{ marginTop: 20, marginBottom: 10, }} onClick={() => {
|
||||
if (window.drift !== undefined) {
|
||||
@@ -1204,12 +1206,13 @@ const Billing = memo((props) => {
|
||||
ReactGA.event({
|
||||
category: "Billing",
|
||||
action: "Buy_consultation_hours",
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const url = userdata.licensed === true
|
||||
? "https://book.stripe.com/6oEeY23fw2T84M06oq"
|
||||
: "https://book.stripe.com/00g6rw3fw9hwguI289";
|
||||
window.open(url, "_blank");
|
||||
? "https://buy.stripe.com/aEU3fk17o0L092g5kt"
|
||||
: "https://buy.stripe.com/aEU3fk17o0L092g5kt"
|
||||
window.open(url, "_blank")
|
||||
}}
|
||||
>
|
||||
Book Professional Service Hours
|
||||
|
||||
@@ -302,6 +302,7 @@ const EditWorkflow = (props) => {
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{}}
|
||||
id="save_workflow_button"
|
||||
disabled={name.length === 0 || submitLoading === true}
|
||||
onClick={() => {
|
||||
setSubmitLoading(true)
|
||||
@@ -406,6 +407,7 @@ const EditWorkflow = (props) => {
|
||||
label="Name"
|
||||
autoFocus
|
||||
fullWidth
|
||||
id="Enter-Workflow-Name"
|
||||
/>
|
||||
|
||||
<div style={{ display: "flex", marginTop: 10, }}>
|
||||
|
||||
@@ -57,7 +57,7 @@ const ExpandMoreAndLessIcon = "/icons/expandMoreIcon.svg";
|
||||
const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const {setLeftSideBarOpenByClick, leftSideBarOpenByClick, setSearchBarModalOpen, searchBarModalOpen} = useContext(Context);
|
||||
const {setLeftSideBarOpenByClick, leftSideBarOpenByClick, setSearchBarModalOpen, searchBarModalOpen, isDocSearchModalOpen} = useContext(Context);
|
||||
|
||||
const [expandLeftNav, setExpandLeftNav] = useState(false);
|
||||
const [activeOrgName, setActiveOrgName] = useState(
|
||||
@@ -526,7 +526,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
<Divider style={{ marginBottom: 10, }} />
|
||||
|
||||
<Typography color="textSecondary" align="center" style={{ marginTop: 5, marginBottom: 5, fontSize: 18 }}>
|
||||
Version: 2.0.0
|
||||
Version: 2.0.1
|
||||
</Typography>
|
||||
</Menu>
|
||||
</span>
|
||||
@@ -773,6 +773,9 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
|
||||
}, [window?.location?.pathname]);
|
||||
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -785,12 +788,13 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
transition: "width 0.3s ease",
|
||||
boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.2)" ,
|
||||
resize: 'both',
|
||||
|
||||
zoom: 0.8,
|
||||
zoom: isSafari ? undefined : 0.8,
|
||||
transform: isSafari ? "scale(0.8)" : undefined,
|
||||
transformOrigin: isSafari ? "top left" : undefined,
|
||||
height: "calc((100vh - 32px)*1.2)",
|
||||
}}
|
||||
>
|
||||
{searchBarModalOpen ? <ModalView serverside={serverside} userdata={userdata} searchBarModalOpen={searchBarModalOpen} setSearchBarModalOpen={setSearchBarModalOpen} globalUrl={globalUrl} /> : null}
|
||||
{searchBarModalOpen && !isDocSearchModalOpen ? <ModalView serverside={serverside} userdata={userdata} searchBarModalOpen={searchBarModalOpen} setSearchBarModalOpen={setSearchBarModalOpen} globalUrl={globalUrl} isDocSearchModalOpen={isDocSearchModalOpen} /> : null}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
@@ -1911,11 +1915,11 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
};
|
||||
|
||||
export default LeftSideBar;
|
||||
const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, serverside, userdata}) => {
|
||||
const ModalView = memo(({searchBarModalOpen, setSearchBarModalOpen, globalUrl, serverside, userdata, isDocSearchModalOpen}) => {
|
||||
return (
|
||||
(
|
||||
<Dialog
|
||||
open={searchBarModalOpen}
|
||||
open={searchBarModalOpen && !isDocSearchModalOpen}
|
||||
onClose={() => {
|
||||
setSearchBarModalOpen(false);
|
||||
}}
|
||||
|
||||
@@ -909,21 +909,23 @@ const LicencePopup = (props) => {
|
||||
const priceItem =
|
||||
window.location.origin === "https://shuffler.io/" || "https://sandbox.shuffler.io/"
|
||||
? shuffleVariant === 0
|
||||
? "app_executions"
|
||||
: "cores"
|
||||
? "price_1PWI3uDzMUgUjxHSffUBwWCy"
|
||||
: "price_1PWI8EDzMUgUjxHSfEhUB7oL"
|
||||
|
||||
: 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`
|
||||
const quantity = shuffleVariant === 0 ? selectedValue / 100 : selectedValue
|
||||
|
||||
console.log("Priceitem: ", priceItem, shuffleVariant)
|
||||
console.log("Priceitem: ", priceItem, quantity, shuffleVariant)
|
||||
var checkoutObject = {
|
||||
lineItems: [
|
||||
{
|
||||
price: priceItem,
|
||||
quantity: shuffleVariant === 0 ? selectedValue / 100 : selectedValue,
|
||||
quantity: quantity,
|
||||
},
|
||||
],
|
||||
mode: "subscription",
|
||||
@@ -1141,6 +1143,7 @@ const LicencePopup = (props) => {
|
||||
>
|
||||
View all plans
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: 4, textTransform: "capitalize", color: "#1a1a1a", backgroundColor: "#ff8544", width: "100%", fontSize: 16}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
AppBar,
|
||||
@@ -35,6 +35,7 @@ import { toast } from "react-toastify";
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import Mousetrap from "mousetrap";
|
||||
import LicencePopup from "../components/LicencePopup.jsx";
|
||||
import { Context } from "../context/ContextApi.jsx";
|
||||
|
||||
const curpath = (typeof window !== "undefined" && window.location && typeof window.location.pathname === "string")
|
||||
? window.location.pathname
|
||||
@@ -47,7 +48,7 @@ const menuData = {
|
||||
title: "Shuffle",
|
||||
description:
|
||||
"The most versatile automation engine with focus on security.",
|
||||
icon: "images/icons/shuffleLogo.svg",
|
||||
icon: "/images/icons/shuffleLogo.svg",
|
||||
path: "/docs/about",
|
||||
gaData: {
|
||||
category: "navbar",
|
||||
@@ -750,7 +751,7 @@ const Navbar = (props) => {
|
||||
const topbar_var = "topbar_closed10"
|
||||
|
||||
const theme = useTheme();
|
||||
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
|
||||
const {searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen} = useContext(Context)
|
||||
const [pricingModalOpen, setPricingModalOpen] = useState(false);
|
||||
const isTabletOrMobile = useMediaQuery(theme.breakpoints.down("lg"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
|
||||
@@ -845,7 +846,7 @@ const Navbar = (props) => {
|
||||
|
||||
const modalView = (
|
||||
<Dialog
|
||||
open={searchBarModalOpen}
|
||||
open={searchBarModalOpen && !isDocSearchModalOpen}
|
||||
onClose={() => {
|
||||
setSearchBarModalOpen(false);
|
||||
}}
|
||||
@@ -1287,10 +1288,21 @@ const Navbar = (props) => {
|
||||
<Button
|
||||
onMouseEnter={() => handleMenuOpen(item)}
|
||||
onMouseLeave={(e) => {
|
||||
// Only close if not moving to dropdown
|
||||
|
||||
const dropdown = document.querySelector('.dropdown-content'); // Adjust the selector as needed
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
const dropdownRect = dropdown ? dropdown.getBoundingClientRect() : null;
|
||||
|
||||
// Check if the cursor is moving down
|
||||
const isMovingDown = e.clientY > rect.bottom;
|
||||
if (!isMovingDown) {
|
||||
|
||||
// Check if the cursor is within the dropdown area or a buffer zone
|
||||
const isOverDropdown = dropdownRect && (
|
||||
(e.clientY >= dropdownRect.top - 20 && e.clientY <= dropdownRect.bottom + 20) && // Buffer zone
|
||||
(e.clientX >= dropdownRect.left && e.clientX <= dropdownRect.right)
|
||||
);
|
||||
|
||||
if (!isMovingDown && !isOverDropdown) {
|
||||
handleMenuClose();
|
||||
}
|
||||
}}
|
||||
@@ -1352,6 +1364,11 @@ const Navbar = (props) => {
|
||||
onClick={() => {
|
||||
if(isCloud) {
|
||||
navigate("/pricing");
|
||||
ReactGA.event({
|
||||
category: "navbar",
|
||||
action: "pricing_click",
|
||||
label: "go_to_pricing",
|
||||
})
|
||||
} else {
|
||||
window.open("https://shuffler.io/pricing", '_blank');
|
||||
return;
|
||||
@@ -1365,6 +1382,11 @@ const Navbar = (props) => {
|
||||
onClick={() => {
|
||||
if(isCloud) {
|
||||
navigate("/partners");
|
||||
ReactGA.event({
|
||||
category: "navbar",
|
||||
action: "partners_click",
|
||||
label: "go_to_partners",
|
||||
})
|
||||
} else {
|
||||
window.open("https://shuffler.io/partners", '_blank');
|
||||
return;
|
||||
@@ -1722,7 +1744,11 @@ const Navbar = (props) => {
|
||||
}}
|
||||
>
|
||||
{topbar}
|
||||
<Container maxWidth="xl">
|
||||
<Container maxWidth="xl" sx={{ zoom: {
|
||||
md: "0.7",
|
||||
lg: "0.8",
|
||||
xl: "1",
|
||||
} }}>
|
||||
<Toolbar disableGutters sx={{ justifyContent: "space-between" }}>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Link
|
||||
|
||||
@@ -307,17 +307,16 @@ const OrgHeaderexpandedNew = (props) => {
|
||||
const setSelectedRegion = (region) => {
|
||||
|
||||
// send a POST request to /api/v1/orgs/{org_id}/region with the region as the body
|
||||
if (region === "US") {
|
||||
region = "us-west2"
|
||||
} else if (region === "EU") {
|
||||
region = "europe-west2"
|
||||
} else if (region === "CA") {
|
||||
region = "northamerica-northeast1"
|
||||
} else if (region === "UK") {
|
||||
region = "europe-west2"
|
||||
} else if (region === "EU-2") {
|
||||
region = "europe-west3"
|
||||
}
|
||||
const regionMap = {
|
||||
"US": "us-west2",
|
||||
"EU": "europe-west2",
|
||||
"CA": "northamerica-northeast1",
|
||||
"UK": "europe-west2",
|
||||
"EU-2": "europe-west3",
|
||||
"AUS": "australia-southeast1"
|
||||
};
|
||||
|
||||
region = regionMap[region] || region;
|
||||
|
||||
var data = {
|
||||
dst_region: region
|
||||
@@ -336,7 +335,11 @@ const OrgHeaderexpandedNew = (props) => {
|
||||
timeOut: 1000
|
||||
}).then((response) => {
|
||||
if (response.status !== 200) {
|
||||
toast("Failed to change region!")
|
||||
response.json().then((reason) => {
|
||||
toast.error("Failed to change region: " + reason.reason)
|
||||
}).catch((err) => {
|
||||
toast.error("Failed to change region")
|
||||
});
|
||||
}
|
||||
else {
|
||||
toast("Region changed successfully! Reloading in 5 seconds..")
|
||||
@@ -347,7 +350,7 @@ const OrgHeaderexpandedNew = (props) => {
|
||||
|
||||
}
|
||||
|
||||
return response.json();
|
||||
// return responseJson
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1091,6 +1094,7 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
|
||||
"EU-2": "eu",
|
||||
"CA": "ca",
|
||||
"UK": "gb",
|
||||
"AUS": "au",
|
||||
};
|
||||
|
||||
//let regiontag = "UK";
|
||||
@@ -1112,6 +1116,9 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
|
||||
} else if (regiontag === "ca") {
|
||||
regiontag = "CA";
|
||||
regionCode = "ca";
|
||||
} else if (regiontag === "austrailia") {
|
||||
regiontag = "AUS";
|
||||
regionCode = "au"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1124,11 +1131,12 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
|
||||
value={regiontag}
|
||||
style={{ minWidth: 120, height: 35, borderRadius: 4 }}
|
||||
onChange={(e) => {
|
||||
if (userdata?.support) {
|
||||
setSelectedRegion(e.target.value)
|
||||
} else {
|
||||
handleSendChangeRegionMail(e.target.value)
|
||||
}
|
||||
// if (userdata?.support) {
|
||||
// setSelectedRegion(e.target.value)
|
||||
// } else {
|
||||
// handleSendChangeRegionMail(e.target.value)
|
||||
// }
|
||||
setSelectedRegion(e.target.value)
|
||||
}}
|
||||
>
|
||||
{Object.keys(regionMapping).map((region, index) => {
|
||||
@@ -1138,6 +1146,10 @@ const RegionChangeModal = memo(({ selectedOrganization, setSelectedRegion, userd
|
||||
selectedOrganization.region = "europe-west2";
|
||||
}
|
||||
|
||||
if (region === "AUS") {
|
||||
region = "AUS (test)"
|
||||
}
|
||||
|
||||
// Check if the current region matches the selected region
|
||||
if (region === selectedOrganization.region) {
|
||||
// If the region matches, set the MenuItem as selected
|
||||
|
||||
@@ -2551,7 +2551,7 @@ const ParsedAction = (props) => {
|
||||
|
||||
{workflow.execution_variables !== undefined && workflow.execution_variables !== null && workflow.execution_variables.length > 0 ? (
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
<Typography>Execution variable (optional)</Typography>
|
||||
<Typography color="textSecondary">Runtime variable (optional)</Typography>
|
||||
<Select
|
||||
MenuProps={{
|
||||
disableScrollLock: true,
|
||||
|
||||
@@ -51,12 +51,13 @@ const searchClient = algoliasearch("JNSS5CFDZZ", "db08e40265e2941b9a7d8f644b6e52
|
||||
const SearchData = props => {
|
||||
const { serverside, globalUrl, userdata } = props
|
||||
let navigate = useNavigate();
|
||||
const { searchBarModalOpen, setSearchBarModalOpen } = useContext(Context);
|
||||
const { searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen } = useContext(Context);
|
||||
const borderRadius = 3
|
||||
const node = useRef()
|
||||
const [searchOpen, setSearchOpen] = useState(false)
|
||||
const [oldPath, setOldPath] = useState("")
|
||||
const [value, setValue] = useState("");
|
||||
const isDocSearchModal = isDocSearchModalOpen;
|
||||
|
||||
const handleLinkClick = () => {
|
||||
if (searchBarModalOpen) {
|
||||
@@ -90,6 +91,8 @@ const SearchData = props => {
|
||||
const [inputValue, setInputValue] = useState(currentRefinement);
|
||||
|
||||
const textFieldRef = useRef(null);
|
||||
const debounceTimeoutRef = useRef(null);
|
||||
|
||||
const keyPressHandler = (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
@@ -110,10 +113,25 @@ const SearchData = props => {
|
||||
}, [searchOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentRefinement !== inputValue) {
|
||||
refine(inputValue);
|
||||
// Clear any existing timeout to prevent multiple executions
|
||||
if (debounceTimeoutRef.current) {
|
||||
clearTimeout(debounceTimeoutRef.current);
|
||||
}
|
||||
}, [currentRefinement]);
|
||||
|
||||
// Set a new timeout that will execute the search after a 200ms delay
|
||||
debounceTimeoutRef.current = setTimeout(() => {
|
||||
refine(inputValue);
|
||||
setSearchOpen(inputValue.trim() !== '');
|
||||
}, 200);
|
||||
|
||||
// Cleanup function that runs when component unmounts or when dependencies change
|
||||
// This ensures we don't have any hanging timeouts
|
||||
return () => {
|
||||
if (debounceTimeoutRef.current) {
|
||||
clearTimeout(debounceTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, [inputValue, refine]); // Effect runs when inputValue or refine function changes
|
||||
|
||||
return (
|
||||
|
||||
@@ -122,7 +140,7 @@ const SearchData = props => {
|
||||
>
|
||||
<TextField
|
||||
fullWidth
|
||||
style={{ zIndex: 1100, marginTop: -20, marginBottom: 200, position: "fixed", backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, width: 685, }}
|
||||
style={{ zIndex: 1100, marginTop: -20, marginBottom: 200, position: "fixed", backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, width: 690, }}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
@@ -149,7 +167,7 @@ const SearchData = props => {
|
||||
autoComplete='off'
|
||||
type="search"
|
||||
color="primary"
|
||||
placeholder="Find Public Apps, Workflows, Documentation..."
|
||||
placeholder={isDocSearchModal ? "Type to search documentation..." : "Find Public Apps, Workflows, Documentation..."}
|
||||
value={inputValue}
|
||||
id="shuffle_search_field"
|
||||
onClick={(event) => {
|
||||
@@ -163,12 +181,6 @@ const SearchData = props => {
|
||||
onChange={(event) => {
|
||||
const newValue = event.target.value;
|
||||
setInputValue(newValue);
|
||||
refine(newValue);
|
||||
if (newValue.trim() !== '') {
|
||||
setSearchOpen(true);
|
||||
} else {
|
||||
setSearchOpen(false);
|
||||
}
|
||||
}}
|
||||
onKeyDown={keyPressHandler}
|
||||
inputRef={textFieldRef}
|
||||
@@ -605,7 +617,11 @@ const SearchData = props => {
|
||||
}
|
||||
|
||||
if (hits.length > 4) {
|
||||
hits = hits.slice(0, 4)
|
||||
if(!isDocSearchModal) {
|
||||
hits = hits.slice(0, 4)
|
||||
} else {
|
||||
hits = hits.slice(0, 15)
|
||||
}
|
||||
}
|
||||
|
||||
const type = "documentation"
|
||||
@@ -614,15 +630,19 @@ const SearchData = props => {
|
||||
//console.log(type, hits.length, hits)
|
||||
|
||||
return (
|
||||
<Card elevation={0} style={{ marginRight: 10, marginTop: 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 470, boxShadows: "none", }}>
|
||||
<Card elevation={0} style={{ marginRight: 10, marginTop: isDocSearchModal ? 0 : 50, color: "white", zIndex: 1002, backgroundColor: theme.palette.inputColor, width: "100%", left: 470, boxShadows: "none", }}>
|
||||
{/* <IconButton style={{ zIndex: 5000, position: "absolute", right: 14, color: "grey" }} onClick={() => {
|
||||
setSearchOpen(false)
|
||||
}}>
|
||||
<CloseIcon />
|
||||
</IconButton> */}
|
||||
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color: "#FF8444", borderBottom: "1px solid", width: 152 }}>
|
||||
Documentation
|
||||
</Typography>
|
||||
{
|
||||
!isDocSearchModal && (
|
||||
<Typography variant="h6" style={{ margin: "10px 10px 0px 20px", color: "#FF8444", borderBottom: "1px solid", width: 152 }}>
|
||||
Documentation
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
{/*
|
||||
<IconButton edge="end" aria-label="delete" style={{position: "absolute", top: 5, right: 15,}} onClick={() => {
|
||||
setSearchOpen(false)
|
||||
@@ -630,7 +650,7 @@ const SearchData = props => {
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
*/}
|
||||
<List style={{ backgroundColor: theme.palette.inputColor, }}>
|
||||
<List style={{ backgroundColor: theme.palette.inputColor, marginTop: isDocSearchModal ? 35 : 0, }}>
|
||||
{hits.length === 0 ?
|
||||
<ListItem style={outerlistitemStyle}>
|
||||
<ListItemAvatar onClick={() => console.log(hits)}>
|
||||
@@ -654,8 +674,8 @@ const SearchData = props => {
|
||||
cursor: "pointer",
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
maxHeight: 75,
|
||||
minHeight: 75,
|
||||
maxHeight: isDocSearchModal ? 100 : 75,
|
||||
minHeight: isDocSearchModal ? 100 : 75,
|
||||
maxWidth: 420,
|
||||
minWidth: "100%",
|
||||
}
|
||||
@@ -666,9 +686,13 @@ const SearchData = props => {
|
||||
(hit.name.charAt(0).toUpperCase() + hit.name.slice(1)).replaceAll("_", " ")
|
||||
|
||||
if (name.length > 30) {
|
||||
name = name.slice(0, 30) + "..."
|
||||
if(isDocSearchModal) {
|
||||
name = name.slice(0, 70) + "..."
|
||||
} else {
|
||||
name = name.slice(0, 30) + "..."
|
||||
}
|
||||
}
|
||||
const secondaryText = hit.data !== undefined ? hit.data.slice(0, 40) + "..." : ""
|
||||
const secondaryText = hit.data !== undefined ? hit.data.slice(0, 80) + "..." : ""
|
||||
const avatar = hit.image_url === undefined ?
|
||||
baseImage
|
||||
:
|
||||
@@ -719,7 +743,7 @@ const SearchData = props => {
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={name}
|
||||
secondary={secondaryText}
|
||||
secondary={<div style={{ padding: '8px 0' }}>{secondaryText}</div>}
|
||||
/>
|
||||
{/*
|
||||
<ListItemSecondaryAction>
|
||||
@@ -897,28 +921,38 @@ const SearchData = props => {
|
||||
|
||||
const modalView = (
|
||||
<div>
|
||||
<Grid container style={{ display: "contents", }}>
|
||||
<Grid item xs="auto" style={{}}>
|
||||
<Index indexName="appsearch">
|
||||
<CustomAppHits />
|
||||
</Index>
|
||||
{
|
||||
!isDocSearchModal ? (
|
||||
<Grid container style={{ display: "contents", }}>
|
||||
<Grid item xs="auto" style={{}}>
|
||||
<Index indexName="appsearch">
|
||||
<CustomAppHits />
|
||||
</Index>
|
||||
</Grid>
|
||||
<Grid item xs="auto" style={{}}>
|
||||
<Index indexName="workflows">
|
||||
<CustomWorkflowHits />
|
||||
</Index>
|
||||
</Grid>
|
||||
<Grid item xs="auto" style={{}}>
|
||||
<Index indexName="documentation">
|
||||
<CustomDocHits />
|
||||
</Index>
|
||||
</Grid>
|
||||
<Grid item xs="auto" style={{}}>
|
||||
<Index indexName="discord_chat">
|
||||
<CustomDiscordHits />
|
||||
</Index>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs="auto" style={{}}>
|
||||
<Index indexName="workflows">
|
||||
<CustomWorkflowHits />
|
||||
</Index>
|
||||
</Grid>
|
||||
<Grid item xs="auto" style={{}}>
|
||||
<Index indexName="documentation">
|
||||
<CustomDocHits />
|
||||
</Index>
|
||||
</Grid>
|
||||
<Grid item xs="auto" style={{}}>
|
||||
<Index indexName="discord_chat">
|
||||
<CustomDiscordHits />
|
||||
</Index>
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : (
|
||||
<Grid item xs="auto" style={{width: "100%"}}>
|
||||
<Index indexName="documentation">
|
||||
<CustomDocHits />
|
||||
</Index>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -932,7 +966,7 @@ const SearchData = props => {
|
||||
<CustomSearchBox />
|
||||
{modalView}
|
||||
</InstantSearch>
|
||||
{gettingStartData}
|
||||
{!isDocSearchModal && gettingStartData}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2476,7 +2476,8 @@ const CodeEditor = (props) => {
|
||||
var fixedcodedata = localcodedata
|
||||
const valid = validateJson(localcodedata, true)
|
||||
if (valid.valid) {
|
||||
fixedcodedata = JSON.stringify(valid.result, null, 2)
|
||||
//fixedcodedata = JSON.stringify(valid.result, null, 2)
|
||||
fixedcodedata = JSON.stringify(valid.result)
|
||||
}
|
||||
|
||||
// console.log(codedata)
|
||||
|
||||
@@ -398,8 +398,9 @@ const WorkflowValidationTimeline = (props) => {
|
||||
nodecolor = green
|
||||
branchcolor = green
|
||||
} else {
|
||||
nodecolor = grey
|
||||
branchcolor = grey
|
||||
// Green by default
|
||||
nodecolor = green
|
||||
branchcolor = green
|
||||
}
|
||||
} else {
|
||||
nodecolor = green
|
||||
@@ -561,8 +562,6 @@ const WorkflowValidationTimeline = (props) => {
|
||||
nodecolor = green
|
||||
branchcolor = green
|
||||
}
|
||||
|
||||
|
||||
|
||||
var flex = index !== 0 && index !== relevantactions.length - 1 ? 1 : 3
|
||||
if (nodecolor === green) {
|
||||
@@ -609,6 +608,8 @@ const WorkflowValidationTimeline = (props) => {
|
||||
const ballsize = 8
|
||||
const topMargin = 20
|
||||
|
||||
console.log("CHIP: ", index, chipColor, chipBackground)
|
||||
|
||||
const chipStyle = {
|
||||
height: 40,
|
||||
minWidth: 125,
|
||||
@@ -676,7 +677,6 @@ const WorkflowValidationTimeline = (props) => {
|
||||
{founderror.length > 0 ? founderror : `App: ${appname} - Action: ${action.label}`}
|
||||
</Typography>
|
||||
} placement="top">
|
||||
|
||||
<Chip label={`${appname}`} style={chipStyle} icon={
|
||||
<Avatar
|
||||
variant="round"
|
||||
|
||||
@@ -7,11 +7,7 @@ import {
|
||||
Tooltip,
|
||||
TextField,
|
||||
Grid,
|
||||
Skeleton,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
Box,
|
||||
Checkbox
|
||||
} from "@mui/material";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { Link } from "react-router-dom";
|
||||
@@ -30,6 +26,8 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
const classes = useStyles();
|
||||
const [show2faSetup, setShow2faSetup] = React.useState(false);
|
||||
const [autoPrivision, setAutoProvision] = React.useState(selectedOrganization?.sso_config?.auto_provision)
|
||||
const [showOpenIdCred, setShowOpenIdCred] = React.useState(false);
|
||||
const [showSamlCred, setShowSamlCred] = React.useState(false);
|
||||
const [ssoEntrypoint, setSsoEntrypoint] = React.useState(
|
||||
selectedOrganization.sso_config === undefined
|
||||
? ""
|
||||
@@ -393,7 +391,27 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
</span>
|
||||
<Typography style={{ textAlign: "left", fontSize: 16, marginTop: 8, color: "rgba(158, 158, 158, 1)", fontWeight: 400 }}>
|
||||
IdP URL for Shuffle OpenID: <Link to={`${globalUrl}/api/v1/login_openid`} target="_blank" style={{ color: "rgba(241, 241, 241, 1)", textDecoration: "none", fontSize: 16,}}>{`${globalUrl}/api/v1/login_openid`}</Link>
|
||||
</Typography>
|
||||
</Typography>
|
||||
<div style={{ display: 'flex', marginTop: 10, }}>
|
||||
<Typography
|
||||
style={{
|
||||
textAlign: "left",
|
||||
fontSize: 16,
|
||||
marginTop: 8,
|
||||
color: "rgba(158, 158, 158, 1)",
|
||||
fontWeight: 400,
|
||||
}}
|
||||
>
|
||||
Show OpenID Credentials
|
||||
</Typography>
|
||||
<Checkbox
|
||||
checked={showOpenIdCred}
|
||||
onChange={(e) => setShowOpenIdCred(e.target.checked)}
|
||||
name="showOpenIdCred"
|
||||
color="primary"
|
||||
style={{ color: "rgba(255, 255, 255, 1)", }}
|
||||
/>
|
||||
</div>
|
||||
<Grid container style={{ marginTop: 8, }} spacing={2}>
|
||||
<Grid item xs={6} style={{}}>
|
||||
<span>
|
||||
@@ -407,30 +425,29 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
multiline={true}
|
||||
rows={2}
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
placeholder="The OpenID client ID from the identity provider"
|
||||
value={openidClientId}
|
||||
onChange={(e) => {
|
||||
setOpenidClientId(e.target.value);
|
||||
}}
|
||||
value={showOpenIdCred ? openidClientId : openidClientId?.length > 0 ? "•".repeat(50) : ""}
|
||||
onChange={(e) => setOpenidClientId(e.target.value)}
|
||||
onFocus={(e) => setShowOpenIdCred(true)}
|
||||
onBlur={(e) => setShowOpenIdCred(false)}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||
},
|
||||
style: {
|
||||
color: "white",
|
||||
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
|
||||
fontWeight: 400,
|
||||
fontSize: 16,
|
||||
borderRadius: 4,
|
||||
color: "white",
|
||||
fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)",
|
||||
fontWeight: 400,
|
||||
fontSize: 16,
|
||||
borderRadius: 4,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
/>
|
||||
</span>
|
||||
</Grid>
|
||||
<Grid item xs={6} style={{}}>
|
||||
@@ -445,17 +462,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
type={showOpenIdCred ? "text" : "password"}
|
||||
multiline={true}
|
||||
rows={2}
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
placeholder="The OpenID client secret - DONT use this if dealing with implicit auth / PKCE"
|
||||
value={openidClientSecret}
|
||||
value={showOpenIdCred ? openidClientSecret : openidClientSecret?.length > 0 ? "•".repeat(50) : ""}
|
||||
onChange={(e) => {
|
||||
setOpenidClientSecret(e.target.value);
|
||||
}}
|
||||
}}
|
||||
onFocus={(e) => setShowOpenIdCred(true)}
|
||||
onBlur={(e) => setShowOpenIdCred(false)}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||
@@ -485,17 +504,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
type={showOpenIdCred ? "text" : "password"}
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
multiline={true}
|
||||
rows={2}
|
||||
placeholder="The OpenID authorization URL (usually ends with /authorize)"
|
||||
value={openidAuthorization}
|
||||
value={showOpenIdCred ? openidAuthorization : openidAuthorization?.length > 0 ? "•".repeat(50) : ""}
|
||||
onChange={(e) => {
|
||||
setOpenidAuthorization(e.target.value)
|
||||
}}
|
||||
}}
|
||||
onFocus={(e) => setShowOpenIdCred(true)}
|
||||
onBlur={(e) => setShowOpenIdCred(false)}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||
@@ -523,17 +544,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
type={showOpenIdCred ? "text" : "password"}
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
multiline={true}
|
||||
rows={2}
|
||||
placeholder="The OpenID token URL (usually ends with /token)"
|
||||
value={openidToken}
|
||||
value={showOpenIdCred ? openidToken : openidToken?.length > 0 ? "•".repeat(50) : ""}
|
||||
onChange={(e) => {
|
||||
setOpenidToken(e.target.value)
|
||||
}}
|
||||
}}
|
||||
onFocus={(e) => setShowOpenIdCred(true)}
|
||||
onBlur={(e) => setShowOpenIdCred(false)}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||
@@ -557,7 +580,27 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
<Typography variant="h4" style={{ textAlign: "left", color: "rgba(241, 241, 241, 1)", fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 24, fontWeight: 600, }}>SAML SSO (v1.1)</Typography>
|
||||
<Typography variant="body2" style={{ textAlign: "left", marginTop: 8, color: "rgba(241, 241, 241, 1)", fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontSize: 16, fontWeight: 400, color: "rgba(158, 158, 158, 1)" }} color="textSecondary">
|
||||
IdP URL for Shuffle SAML/SSO: <Link to={`${globalUrl}/api/v1/login_sso`} target="_blank" style={{ color: "rgba(241, 241, 241, 1)", textDecoration: "none" }}>{`${globalUrl}/api/v1/login_sso`}</Link>
|
||||
</Typography>
|
||||
</Typography>
|
||||
<div style={{ display: 'flex', marginTop: 10, }}>
|
||||
<Typography
|
||||
style={{
|
||||
textAlign: "left",
|
||||
fontSize: 16,
|
||||
marginTop: 8,
|
||||
color: "rgba(158, 158, 158, 1)",
|
||||
fontWeight: 400,
|
||||
}}
|
||||
>
|
||||
Show SAML Credentials
|
||||
</Typography>
|
||||
<Checkbox
|
||||
checked={showSamlCred}
|
||||
onChange={(e) => setShowSamlCred(e.target.checked)}
|
||||
name="showSamlCred"
|
||||
color="primary"
|
||||
style={{ color: "rgba(255, 255, 255, 1)", }}
|
||||
/>
|
||||
</div>
|
||||
<Grid container style={{ marginTop: 10, }} spacing={2}>
|
||||
<Grid item xs={6} style={{}}>
|
||||
<span>
|
||||
@@ -571,17 +614,19 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
type={showSamlCred ? "text" : "password"}
|
||||
multiline={true}
|
||||
rows={2}
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
placeholder="The entrypoint URL from your provider"
|
||||
value={ssoEntrypoint}
|
||||
value={showSamlCred ? ssoEntrypoint : ssoEntrypoint?.length > 0 ? "•".repeat(50) : ""}
|
||||
onChange={(e) => {
|
||||
setSsoEntrypoint(e.target.value);
|
||||
}}
|
||||
}}
|
||||
onFocus={(e) => setShowSamlCred(true)}
|
||||
onBlur={(e) => setShowSamlCred(false)}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||
@@ -609,14 +654,16 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||
}}
|
||||
fullWidth={true}
|
||||
type="name"
|
||||
type={showSamlCred ? "text" : "password"}
|
||||
id="outlined-with-placeholder"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
multiline={true}
|
||||
rows={2}
|
||||
placeholder="The X509 certificate to use"
|
||||
value={ssoCertificate}
|
||||
value={showSamlCred ? ssoCertificate : ssoCertificate?.length > 0 ? "•".repeat(50) : ""}
|
||||
onFocus={(e) => setShowSamlCred(true)}
|
||||
onBlur={(e) => setShowSamlCred(false)}
|
||||
onChange={(e) => {
|
||||
setSsoCertificate(e.target.value);
|
||||
}}
|
||||
@@ -646,4 +693,4 @@ const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handle
|
||||
)
|
||||
}
|
||||
|
||||
export default SSOTab
|
||||
export default SSOTab
|
||||
|
||||
@@ -8,6 +8,7 @@ export const AppContext = (props) => {
|
||||
|
||||
// Left side bar global states
|
||||
const [searchBarModalOpen, setSearchBarModalOpen] = useState(false);
|
||||
const [isDocSearchModalOpen, setIsDocSearchModalOpen] = useState(false);
|
||||
const [leftSideBarOpenByClick, setLeftSideBarOpenByClick] = useState(currentLocation?.includes('/workflows/') ? false : true)
|
||||
const [windowWidth, setWindowWidth] = useState(serverside === true ? 100 : window.innerWidth);
|
||||
|
||||
@@ -37,6 +38,8 @@ export const AppContext = (props) => {
|
||||
|
||||
return (
|
||||
<Context.Provider value={{
|
||||
isDocSearchModalOpen,
|
||||
setIsDocSearchModalOpen,
|
||||
searchBarModalOpen,
|
||||
setSearchBarModalOpen,
|
||||
leftSideBarOpenByClick,
|
||||
|
||||
@@ -81,7 +81,7 @@ const theme = createTheme(adaptV4Theme({
|
||||
singulBlackWhite: "/images/singul_black_white.png",
|
||||
},
|
||||
typography: {
|
||||
fontFamily: `"Roboto", "Helvetica", "Arial", "inter", sans-serif`,
|
||||
fontFamily: `"inter", "Roboto", "Helvetica", "Arial", sans-serif`,
|
||||
useNextVariants: true,
|
||||
fontWeightLight: 300,
|
||||
fontWeightRegular: 400,
|
||||
|
||||
@@ -386,6 +386,8 @@ const useStyles = makeStyles({
|
||||
|
||||
const splitter = "|~|";
|
||||
const svgSize = 24;
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
|
||||
//const referenceUrl = "https://shuffler.io/functions/webhooks/"
|
||||
//const referenceUrl = window.location.origin+"/api/v1/hooks/"
|
||||
|
||||
@@ -1320,33 +1322,45 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const allTriggersInOne = useTriggers.pipelines.concat(useTriggers.schedules).concat(useTriggers.webhooks)
|
||||
for (let triggerkey in allTriggersInOne) {
|
||||
const curtrigger = allTriggersInOne[triggerkey]
|
||||
if (curtrigger.id === selectedTrigger.id) {
|
||||
if (curtrigger.status === undefined || curtrigger.status === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (curtrigger.running === undefined || curtrigger.running === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
var changed = false
|
||||
if (curtrigger.status !== selectedTrigger.status) {
|
||||
changed = true
|
||||
selectedTrigger.status = curtrigger.status
|
||||
}
|
||||
|
||||
if (curtrigger.running !== selectedTrigger.running) {
|
||||
changed = true
|
||||
selectedTrigger.running = curtrigger.running
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
//console.log("TRIGGER FIX: ", selectedTrigger)
|
||||
setSelectedTrigger(selectedTrigger)
|
||||
}
|
||||
|
||||
break
|
||||
if (curtrigger.id !== selectedTrigger.id) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (curtrigger.status === undefined || curtrigger.status === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
var changed = false
|
||||
if (selectedTrigger?.parameters === undefined || selectedTrigger?.parameters === null) {
|
||||
changed = true
|
||||
|
||||
if (selectedTrigger?.trigger_type === "SCHEDULE" && curtrigger?.frequency !== undefined && curtrigger?.frequency !== null) {
|
||||
selectedTrigger.parameters = [{
|
||||
name: "cron",
|
||||
value: curtrigger.frequency,
|
||||
},
|
||||
{
|
||||
name: "execution_argument",
|
||||
value: curtrigger.argument,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
if (curtrigger.status !== selectedTrigger.status) {
|
||||
changed = true
|
||||
selectedTrigger.status = curtrigger.status
|
||||
}
|
||||
|
||||
if (curtrigger.running !== selectedTrigger.running) {
|
||||
changed = true
|
||||
selectedTrigger.running = curtrigger.running
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
setSelectedTrigger(selectedTrigger)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2799,11 +2813,11 @@ const AngularWorkflow = (defaultprops) => {
|
||||
|
||||
// FIXME: Check if any node contains $exec in a param
|
||||
// If they do, show a popup asking if they want to execute it without an execution argument, or to use a previous one
|
||||
if (skip_popup !== true && executionArgument === undefined || executionArgument === null || executionArgument.length === 0)
|
||||
if (skip_popup !== true && executionArgument === undefined || executionArgument === null || executionArgument?.length === 0)
|
||||
|
||||
if (workflow.actions !== undefined && workflow.actions !== null && workflow.actions.length > 0) {
|
||||
if (workflow?.actions !== undefined && workflow?.actions !== null && workflow?.actions?.length > 0) {
|
||||
var foundmissing = false
|
||||
for (let actionkey in workflow.actions) {
|
||||
for (let actionkey in workflow?.actions) {
|
||||
if (workflow.actions[actionkey].parameters === undefined || workflow.actions[actionkey].parameters === null || workflow.actions[actionkey].parameters.length === 0) {
|
||||
continue
|
||||
}
|
||||
@@ -12236,7 +12250,9 @@ const AngularWorkflow = (defaultprops) => {
|
||||
overflowAnchor: "none",
|
||||
minWidth: minSize,
|
||||
width: isMobile ? "100%" : minSize,
|
||||
zoom: 0.9,
|
||||
zoom: isSafari ? undefined : 0.9,
|
||||
transform: isSafari ? "scale(0.9)" : undefined,
|
||||
transformOrigin: isSafari ? "top right" : undefined,
|
||||
};
|
||||
|
||||
const setTriggerFolderWrapperMulti = (event) => {
|
||||
@@ -18011,10 +18027,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const topBarStyle = {
|
||||
position: "absolute",
|
||||
|
||||
top: isMobile ? 30 : 25,
|
||||
top: isMobile ? 30 : isSafari ? 15 : 25,
|
||||
transform: isMobile ? "translateX(20px)" : `translateX(${leftBarSize}px)`,
|
||||
transition: "all 0.3s ease",
|
||||
zoom: 0.9,
|
||||
zoom: isSafari ? undefined : 0.9,
|
||||
}
|
||||
|
||||
const TopCytoscapeBar = (props) => {
|
||||
@@ -21834,13 +21850,23 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
const chosenNodeId = new URLSearchParams(cursearch).get("node");
|
||||
const highlightNode = chosenNodeId !== null && chosenNodeId !== undefined && chosenNodeId !== "" && chosenNodeId === data.action.id
|
||||
var relevant_errors = []
|
||||
if (data?.action?.parameters !== undefined && data?.action?.parameters !== null && data?.action?.parameters.length > 0) {
|
||||
for (var i = 0; i < data.action.parameters.length; i++) {
|
||||
// Specific error patterns in params
|
||||
const param = data.action.parameters[i]
|
||||
if (param?.name?.endsWith("_error") && (param?.name?.startsWith("shuffle_") || param?.name?.startsWith("liquid_"))) {
|
||||
relevant_errors.push(param)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
marginBottom: 20,
|
||||
border: highlightNode ? "2px solid red"
|
||||
border: highlightNode ? `2px solid ${red}`
|
||||
:
|
||||
data.action.sub_action === true
|
||||
? "1px solid rgba(255,255,255,0.3)"
|
||||
@@ -21913,11 +21939,17 @@ const AngularWorkflow = (defaultprops) => {
|
||||
>
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title="Expand result window"
|
||||
title={
|
||||
<Typography variant="body1">
|
||||
Expand result window. Errors: {relevant_errors.length}
|
||||
</Typography>
|
||||
}
|
||||
placement="top"
|
||||
style={{ zIndex: 50000 }}
|
||||
>
|
||||
<ArrowLeftIcon style={{ color: "white" }} />
|
||||
<ArrowLeftIcon style={{
|
||||
color: relevant_errors.length > 0 ? yellow : "rgba(255,255,255,0.7)",
|
||||
}} />
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
{actionimg}
|
||||
@@ -22087,6 +22119,11 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const checked = validateJson(data.value.trim())
|
||||
|
||||
if (data.name === "shuffle_action_logs" && data.value !== undefined && data.value !== null && data.value.length > 0 && data.value.includes("add env SHUFFLE_LOGS_DISABLED")) {
|
||||
|
||||
if (isCloud) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: 600, marginTop: 75, overflowX: "hidden", }}>
|
||||
<Typography
|
||||
|
||||
+179
-23
@@ -23,7 +23,12 @@ import {
|
||||
List,
|
||||
Collapse,
|
||||
ListItemButton,
|
||||
ListItemText
|
||||
ListItemText,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
Box,
|
||||
DialogContent,
|
||||
InputAdornment
|
||||
} from "@mui/material";
|
||||
import {
|
||||
Link as LinkIcon,
|
||||
@@ -32,9 +37,12 @@ import {
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
FileCopy as FileCopyIcon,
|
||||
ChevronLeft,
|
||||
ChevronRight
|
||||
ChevronRight,
|
||||
Search as SearchIcon,
|
||||
Close as CloseIcon
|
||||
} from "@mui/icons-material";
|
||||
import { fontGrid } from "@mui/material/styles/cssUtils.js";
|
||||
import SearchBox from "../components/SearchData.jsx";
|
||||
|
||||
const Body = {
|
||||
//maxWidth: 1000,
|
||||
@@ -158,12 +166,13 @@ export const OuterLink = (props) => {
|
||||
|
||||
export const Img = (props) => {
|
||||
// Find parent container and check width
|
||||
const isArticlePage = window.location.pathname.includes("/articles/");
|
||||
const isArticlePage = window.location.pathname.includes("/articles/")
|
||||
const isFormPage = window.location.pathname.includes("/forms/")
|
||||
var height = "auto"
|
||||
var width = isArticlePage ? 1000 : 750
|
||||
var width = isArticlePage ? 1000 : isFormPage ? 400: 750
|
||||
|
||||
const docsImageStyle = {
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
border: isFormPage ? null : "1px solid rgba(255,255,255,0.3)",
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
width: width,
|
||||
maxWidth: width,
|
||||
@@ -191,11 +200,13 @@ export const Img = (props) => {
|
||||
}
|
||||
|
||||
return(
|
||||
<img
|
||||
style={isArticlePage ? articleImageStyle : docsImageStyle}
|
||||
alt={props.alt}
|
||||
src={props.src}
|
||||
/>
|
||||
<div style={{width: "100%", textAlign: "center", }}>
|
||||
<img
|
||||
style={isArticlePage ? articleImageStyle : docsImageStyle}
|
||||
alt={props.alt}
|
||||
src={props.src}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -278,7 +289,7 @@ export const CodeHandler = (props) => {
|
||||
const Docs = (defaultprops) => {
|
||||
const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded, userdata } = defaultprops;
|
||||
|
||||
console.log("\n\nSELECTED DOC\n", selectedDoc, "\n\n")
|
||||
const { searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen, setIsDocSearchModalOpen, leftSideBarOpenByClick } = useContext(Context);
|
||||
|
||||
let navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -316,20 +327,36 @@ const Docs = (defaultprops) => {
|
||||
const [activeSubItem, setActiveSubItem] = useState(false);
|
||||
const headingElementsRef = useRef({})
|
||||
var isArticlePage = window.location.pathname.includes("/articles/") || window.location.pathname === "/articles" ? true : false;
|
||||
const searchFieldRef = useRef(null);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
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]);
|
||||
|
||||
useEffect(() => {
|
||||
//if (params["key"] === undefined) {
|
||||
// navigate("/docs/about")
|
||||
// return
|
||||
//}
|
||||
}, [])
|
||||
|
||||
return () => {
|
||||
setIsDocSearchModalOpen(false);
|
||||
setSearchBarModalOpen(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
}
|
||||
@@ -459,6 +486,71 @@ const Docs = (defaultprops) => {
|
||||
return headings;
|
||||
};
|
||||
|
||||
const modalView = (
|
||||
<Dialog
|
||||
open={searchBarModalOpen && isDocSearchModalOpen}
|
||||
onClose={() => {
|
||||
setSearchBarModalOpen(false);
|
||||
if (searchFieldRef.current) {
|
||||
searchFieldRef.current.blur();
|
||||
}
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
minWidth: 750,
|
||||
minHeight: "180px",
|
||||
maxHeight: "85vh",
|
||||
borderRadius: 16,
|
||||
border: "1px solid var(--Container-Stroke, #494949)",
|
||||
background: "var(--Container, #000000)",
|
||||
boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)",
|
||||
position: "fixed",
|
||||
top: "70px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
zIndex: 50005,
|
||||
'& .MuiBackdrop-root': {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", px: 2, pr: 3, pt: 2 }}>
|
||||
<DialogTitle
|
||||
sx={{
|
||||
color: "var(--Paragraph-text, #C8C8C8)",
|
||||
p: 0,
|
||||
m: 0,
|
||||
ml: 1.5,
|
||||
fontFamily: theme.typography.fontFamily,
|
||||
}}
|
||||
>
|
||||
Search for Documentation
|
||||
</DialogTitle>
|
||||
<IconButton
|
||||
onClick={() => setSearchBarModalOpen(false)}
|
||||
sx={{
|
||||
color: 'white',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<DialogContent>
|
||||
<Box sx={{ pt: 3 }}>
|
||||
<SearchBox globalUrl={globalUrl} serverside={serverside} userdata={userdata} />
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<Divider sx={{ backgroundColor: 'rgba(255, 255, 255, 0.1)' }}/>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
|
||||
// extract TOC from actual markdown
|
||||
const tocvalue = (markdown) => {
|
||||
@@ -761,7 +853,13 @@ const Docs = (defaultprops) => {
|
||||
}
|
||||
|
||||
const fetchDocList = (resetCache = false) => {
|
||||
const url = isArticlePage ? `${globalUrl}/api/v1/articles?resetCache=${resetCache}` : `${globalUrl}/api/v1/docs`;
|
||||
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: {
|
||||
@@ -782,8 +880,18 @@ const Docs = (defaultprops) => {
|
||||
.catch((error) => { });
|
||||
};
|
||||
|
||||
const fetchDocs = (docId) => {
|
||||
const url = isArticlePage ? `${globalUrl}/api/v1/articles/${docId}` : `${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: {
|
||||
@@ -902,7 +1010,7 @@ const Docs = (defaultprops) => {
|
||||
});
|
||||
} else {
|
||||
console.log("DOCID: ", props.match.params.key)
|
||||
fetchDocs(props.match.params.key)
|
||||
fetchDoc(props.match.params.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -911,7 +1019,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 = {
|
||||
@@ -1093,9 +1201,50 @@ const Docs = (defaultprops) => {
|
||||
<div style={Body}>
|
||||
<div style={!isArticlePage ? SideBar : (sidebarOpen ? SideBar : collapsedSideBar)}>
|
||||
<Paper style={SidebarPaperStyle}>
|
||||
{modalView}
|
||||
{
|
||||
!isArticlePage && (
|
||||
<TextField
|
||||
variant="outlined"
|
||||
placeholder="Search Docs..."
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setIsDocSearchModalOpen(true)
|
||||
setSearchBarModalOpen(true)
|
||||
}}
|
||||
inputRef={searchFieldRef}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<SearchIcon style={{ color: "white" }} />
|
||||
</InputAdornment>
|
||||
),
|
||||
style: {
|
||||
backgroundColor: "#212121",
|
||||
borderRadius: 4,
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginLeft: 10,
|
||||
borderRadius: 4,
|
||||
width: !isLoggedIn ? "14%" : !leftSideBarOpenByClick ? "12%" : "11%",
|
||||
maxWidth: "285px",
|
||||
position: "fixed",
|
||||
zIndex: 1100,
|
||||
}}
|
||||
inputProps={{
|
||||
readOnly: true,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<List style={{
|
||||
listStyle: "none",
|
||||
paddingLeft: "0",
|
||||
paddingTop: !isArticlePage ? "60px" : undefined,
|
||||
paddingBottom: !isArticlePage ? "30px" : undefined,
|
||||
display: isArticlePage ? sidebarOpen ? "block" : "none" : undefined,
|
||||
opacity: isArticlePage ? sidebarOpen ? 1 : 0 : undefined,
|
||||
transition: isArticlePage ? "opacity 0.3s ease" : undefined,
|
||||
@@ -1106,7 +1255,14 @@ const Docs = (defaultprops) => {
|
||||
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(" ");
|
||||
|
||||
@@ -5,8 +5,8 @@ import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { toast } from 'react-toastify';
|
||||
import { useInterval } from "react-powerhooks";
|
||||
|
||||
|
||||
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
|
||||
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
|
||||
import {
|
||||
ConnectingAirportsOutlined,
|
||||
ConstructionOutlined,
|
||||
@@ -81,21 +81,6 @@ const useStyles = makeStyles({
|
||||
notchedOutline: {
|
||||
borderColor: "#f85a3e !important"
|
||||
},
|
||||
marketplaceButton: {
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
padding: "12px",
|
||||
marginBottom: "12px",
|
||||
backgroundColor: "#1A1A1A",
|
||||
border: "1px solid #494949",
|
||||
borderRadius: "8px",
|
||||
color: "white",
|
||||
opacity: 0.7,
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
cursor: "not-allowed",
|
||||
},
|
||||
},
|
||||
marketplaceIcon: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
@@ -174,6 +159,14 @@ const FreePlanCard = ({ classes }) => {
|
||||
|
||||
const MarketplaceCard = ({ classes }) => {
|
||||
const marketplaceOptions = [
|
||||
{
|
||||
name: "Open Source Install",
|
||||
logo: "https://static.cdnlogo.com/logos/g/69/github-icon.svg",
|
||||
tooltipText: "Click to install!",
|
||||
valid: true,
|
||||
|
||||
link: "https://github.com/shuffle/shuffle/blob/main/.github/install-guide.md"
|
||||
},
|
||||
{
|
||||
name: "Amazon Web Services",
|
||||
logo: "https://cdn.cdnlogo.com/logos/a/19/aws.svg",
|
||||
@@ -213,31 +206,50 @@ const MarketplaceCard = ({ classes }) => {
|
||||
Self Host
|
||||
</h2>
|
||||
|
||||
{marketplaceOptions.map((option, index) => (
|
||||
<Tooltip
|
||||
key={index}
|
||||
title={option.tooltipText}
|
||||
placement="top"
|
||||
arrow
|
||||
>
|
||||
<button
|
||||
className={classes.marketplaceButton}
|
||||
disabled
|
||||
{marketplaceOptions.map((option, index) => {
|
||||
return (
|
||||
<Tooltip
|
||||
key={index}
|
||||
title={option.tooltipText}
|
||||
placement="top"
|
||||
arrow
|
||||
>
|
||||
<img
|
||||
src={option.logo}
|
||||
alt={option.name}
|
||||
className={classes.marketplaceIcon}
|
||||
style={{
|
||||
filter: option.valid === true ? null : "grayscale(1)",
|
||||
<button
|
||||
onClick={() => {
|
||||
if (option.valid === true) {
|
||||
window.open(option.link, "_blank")
|
||||
}
|
||||
|
||||
}}
|
||||
/>
|
||||
<Typography className={classes.marketplaceText}>
|
||||
{option.name}
|
||||
</Typography>
|
||||
</button>
|
||||
</Tooltip>
|
||||
))}
|
||||
style={{
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
padding: "12px",
|
||||
marginBottom: "12px",
|
||||
backgroundColor: "#1A1A1A",
|
||||
border: "1px solid #494949",
|
||||
borderRadius: "8px",
|
||||
color: "white",
|
||||
opacity: 0.7,
|
||||
cursor: option.valid === true ? "pointer" : "not-allowed",
|
||||
}}
|
||||
disabled={option.valid === false}
|
||||
>
|
||||
<img
|
||||
src={option.logo}
|
||||
alt={option.name}
|
||||
className={classes.marketplaceIcon}
|
||||
style={{
|
||||
filter: option.valid === true ? null : "grayscale(1)",
|
||||
}}
|
||||
/>
|
||||
<Typography className={classes.marketplaceText}>
|
||||
{option.name}
|
||||
</Typography>
|
||||
</button>
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
|
||||
<div style={{
|
||||
marginTop: "0.5rem",
|
||||
@@ -256,7 +268,7 @@ const MarketplaceCard = ({ classes }) => {
|
||||
</a>
|
||||
*/}
|
||||
<a
|
||||
href="/docs/configuration#installing-shuffle"
|
||||
href="/docs/configuration#installation"
|
||||
target="_blank"
|
||||
style={{ color: "rgba(255, 132, 68, 1)", textDecoration: "underline" }}
|
||||
>
|
||||
@@ -282,7 +294,7 @@ const LoginPage = props => {
|
||||
const [register, setRegister] = useState(inregister);
|
||||
const [checkboxClicked, setCheckboxClicked] = useState(false);
|
||||
const [loginWithSSO, setLoginWithSSO] = useState(false)
|
||||
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
const [ssoUrl, setSSOUrl] = useState("");
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io";
|
||||
@@ -315,7 +327,15 @@ const LoginPage = props => {
|
||||
} else {
|
||||
document.title = "Register to Shuffle SaaS"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (loginWithSSO && window?.location?.pathname === "/register") {
|
||||
setLoginWithSSO(false)
|
||||
}
|
||||
|
||||
},[window?.location?.pathname])
|
||||
|
||||
// Just a way to force location loading properly
|
||||
// Register & login should be split :3
|
||||
@@ -354,7 +374,8 @@ const LoginPage = props => {
|
||||
color: "white",
|
||||
padding: "40px",
|
||||
flex: 1,
|
||||
maxWidth: isMobile ? "100%" : "550px",
|
||||
maxWidth: isMobile ? "100%" : 410,
|
||||
minWidth: 410,
|
||||
background: "#212121",
|
||||
borderRadius: "12px",
|
||||
display: "flex",
|
||||
@@ -461,7 +482,6 @@ const LoginPage = props => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const onSubmit = (e) => {
|
||||
//toast("Testing from login page")
|
||||
|
||||
@@ -650,26 +670,16 @@ const LoginPage = props => {
|
||||
setLoginWithSSO(true)
|
||||
}
|
||||
|
||||
//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>Welcome Back!</div> : <div>Create your account</div>
|
||||
var formButton = !isCloud ? "" : register ? <div style={{ display: "flex" }}> <div style={{ fontSize: "14px", paddingRight: "7px", textDecoration: "none", }}>Don’t have an account yet?</div> <Link style={hrefStyle} to={`/register${parsedsearch}`}><div>Register here</div></Link></div> : <>
|
||||
|
||||
<div style={{ display: "flex", marginTop: 40, marginBottom: -10 }}> <div style={{ fontSize: "14px", paddingRight: "7px", textDecoration: "none", }}>Already have an account?</div> <Link style={hrefStyle} to={`/login${parsedsearch}`}><div>Login here</div></Link></div>
|
||||
</>
|
||||
//<Link to={`/login${parsedsearch}`} style={hrefStyle}><div>Click here to Login</div></Link>
|
||||
|
||||
// <DialogTitle>{formtitle}</DialogTitle>
|
||||
|
||||
const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)"
|
||||
const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)"
|
||||
|
||||
const buttonStyle = { borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(username, password) || loginLoading || (checkboxClicked && register) ? buttonBackground : "grey", color: "white" }
|
||||
//<Button variant="contained" type="submit" fullWidth style={buttonStyle} disabled={!handleValidateForm() || loginLoading}>
|
||||
@@ -775,15 +785,29 @@ const LoginPage = props => {
|
||||
color: "white",
|
||||
fontSize: "1em",
|
||||
},
|
||||
endAdornment: (
|
||||
<Tooltip title={showPassword ? "Hide password" : "Show password"} arrow>
|
||||
<div
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
marginRight: 10,
|
||||
color: "white"
|
||||
}}
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
>
|
||||
{showPassword ? <VisibilityOutlinedIcon /> : <VisibilityOffOutlinedIcon />}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
}}
|
||||
required
|
||||
id="outlined-password-input"
|
||||
fullWidth={true}
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
placeholder="at least 10 characters"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
type={showPassword ? "text" : "password"}
|
||||
onChange={onChangePass}
|
||||
helperText={
|
||||
handleValidateForm(username, password)
|
||||
@@ -907,7 +931,10 @@ const LoginPage = props => {
|
||||
{loginInfo}
|
||||
</div>
|
||||
|
||||
{ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0 ? (
|
||||
{(
|
||||
ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length) > 0
|
||||
//|| (isCloud && !loginWithSSO && window?.location?.pathname !== "/register")
|
||||
? (
|
||||
<div>
|
||||
<Typography style={{ textAlign: "center" }}>Or</Typography>
|
||||
<div style={{ textAlign: "center", margin: 10 }}>
|
||||
@@ -917,11 +944,17 @@ const LoginPage = props => {
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
type="button"
|
||||
style={{ flex: "1", marginTop: 5 }}
|
||||
onClick={() => {
|
||||
style={{ flex: "1", marginTop: 5, textTransform: 'none', fontSize: 16 }}
|
||||
onClick={(e) => {
|
||||
//console.log("CLICK SSO");
|
||||
window.location.href = ssoUrl
|
||||
e.preventDefault();
|
||||
//navigate(ssoUrl)
|
||||
if (isCloud) {
|
||||
setLoginWithSSO(true)
|
||||
setPassword("")
|
||||
}else {
|
||||
window.location.href = ssoUrl
|
||||
}
|
||||
}}
|
||||
>
|
||||
Use SSO
|
||||
@@ -929,6 +962,19 @@ const LoginPage = props => {
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{isCloud && loginWithSSO && (
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setLoginWithSSO(false)
|
||||
setLoginInfo("")
|
||||
}}
|
||||
style={{ width: '100%', marginTop: 5, flex: "1", textTransform: 'none', fontSize: 16 }}
|
||||
>
|
||||
Back to login
|
||||
</Button>
|
||||
)}
|
||||
</form>
|
||||
</Paper>
|
||||
|
||||
@@ -952,7 +998,7 @@ const LoginPage = props => {
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div style={{zoom: 0.8, paddingTop: 75, paddingBottom: 90 }}>
|
||||
<div style={{zoom: 0.8, minHeight: "75vh", paddingTop: 75, paddingBottom: 90 }}>
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1197,6 +1197,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
<form onSubmit={(e) => {onSubmit(e)}} style={{margin: "25px 0px 15px 0px",}}>
|
||||
{workflowQuestion !== "" || (workflow.form_control.input_markdown !== undefined && workflow.form_control.input_markdown !== null && workflow.form_control.input_markdown.length > 0) ? null :
|
||||
<div>
|
||||
{/*
|
||||
<img
|
||||
alt={workflow.name}
|
||||
src={image}
|
||||
@@ -1211,6 +1212,7 @@ const RunWorkflow = (defaultprops) => {
|
||||
left: 200,
|
||||
}}
|
||||
/>
|
||||
*/}
|
||||
|
||||
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
|
||||
{organization}
|
||||
|
||||
@@ -724,6 +724,8 @@ const Workflows2 = (props) => {
|
||||
|
||||
const [apps, setApps] = React.useState([]);
|
||||
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
|
||||
document.title = "Shuffle - Workflows";
|
||||
|
||||
useEffect(() => {
|
||||
@@ -2327,7 +2329,7 @@ const Workflows2 = (props) => {
|
||||
key={"explore forms"}
|
||||
>
|
||||
<EditNoteIcon style={{ marginLeft: 0, marginRight: 8 }} />
|
||||
{"Create Form"}
|
||||
{"Edit Form"}
|
||||
</MenuItem>
|
||||
|
||||
<Divider />
|
||||
@@ -4289,7 +4291,7 @@ const Workflows2 = (props) => {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
maxWidth: "70%",
|
||||
maxWidth: isSafari ? "100%" : "70%",
|
||||
margin: "auto",
|
||||
}}>
|
||||
<Typography variant="h4" style={{ marginBottom: 20, paddingLeft: 15, textTransform: 'none', fontFamily: theme?.typography?.fontFamily }}>
|
||||
@@ -4616,6 +4618,7 @@ const Workflows2 = (props) => {
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleCreateWorkflow}
|
||||
id="create_workflow_button"
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
flex: 0.8,
|
||||
@@ -5060,8 +5063,9 @@ const Workflows2 = (props) => {
|
||||
</iframe>
|
||||
</Dialog>
|
||||
|
||||
//isLoaded && isLoggedIn && workflowDone ? (
|
||||
const loadedCheck =
|
||||
isLoaded && isLoggedIn && workflowDone ? (
|
||||
workflowDone ? (
|
||||
<div>
|
||||
{/*
|
||||
<ShepherdTour steps={newSteps} tourOptions={tourOptions}>
|
||||
@@ -5128,8 +5132,14 @@ const Workflows2 = (props) => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const safariStyle = {
|
||||
transform: 'scale(0.7)',
|
||||
transformOrigin: 'top',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}
|
||||
// Maybe use gridview or something, idk
|
||||
return <div style={{zoom: 0.7, }}>{loadedCheck}</div>;
|
||||
return <div style={isSafari ? safariStyle : {zoom: 0.7, minHeight: "80vh",}}>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
module orborus
|
||||
|
||||
go 1.22.0
|
||||
toolchain go1.24.1
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.6
|
||||
|
||||
//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared
|
||||
|
||||
@@ -9,7 +10,7 @@ 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.7
|
||||
github.com/shuffle/shuffle-shared v0.8.18
|
||||
k8s.io/api v0.30.2
|
||||
k8s.io/apimachinery v0.30.2
|
||||
)
|
||||
@@ -96,14 +97,14 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
|
||||
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.36.0 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
|
||||
@@ -301,8 +301,6 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc
|
||||
github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||
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.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=
|
||||
@@ -366,8 +364,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.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -423,8 +419,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.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -440,8 +434,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -468,14 +460,10 @@ 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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.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.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -486,8 +474,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
|
||||
@@ -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,42 +769,40 @@ 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, ",") {
|
||||
image = strings.TrimSpace(image)
|
||||
if shuffle.ArrayContains(handled, image) {
|
||||
for _, curimage := range strings.Split(images, ",") {
|
||||
curimage = strings.TrimSpace(curimage)
|
||||
if shuffle.ArrayContains(handled, curimage) {
|
||||
continue
|
||||
}
|
||||
|
||||
handled = append(handled, image)
|
||||
if !strings.Contains(image, "/") {
|
||||
image = fmt.Sprintf("frikky/shuffle:%s", image)
|
||||
handled = append(handled, curimage)
|
||||
if !strings.Contains(curimage, "/") {
|
||||
curimage = fmt.Sprintf("frikky/shuffle:%s", curimage)
|
||||
}
|
||||
|
||||
newImages = append(newImages, image)
|
||||
newImages = append(newImages, curimage)
|
||||
|
||||
// Force remove the current image to avoid cached layers
|
||||
_, err := dockercli.ImageRemove(ctx, curimage, image.RemoveOptions{
|
||||
Force: true,
|
||||
PruneChildren: true,
|
||||
})
|
||||
|
||||
// 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)
|
||||
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 removing image for re-download: %s", err)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Removed image: %s", image)
|
||||
log.Printf("[DEBUG] Removed image: %s", curimage)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
err = shuffle.DownloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, curimage)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed downloading image: %s", err)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Downloaded image: %s", curimage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,20 +821,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 +852,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 +1489,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 +2260,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 +3477,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
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
module worker
|
||||
|
||||
go 1.22.7
|
||||
toolchain go1.24.1
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.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.7
|
||||
github.com/shuffle/shuffle-shared v0.8.18
|
||||
k8s.io/api v0.30.2
|
||||
k8s.io/apimachinery v0.30.2
|
||||
k8s.io/client-go v0.30.2
|
||||
@@ -97,14 +98,14 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
|
||||
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.36.0 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/api v0.162.0 // indirect
|
||||
|
||||
@@ -294,8 +294,6 @@ github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fc
|
||||
github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||
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.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=
|
||||
@@ -359,8 +357,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -415,8 +411,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.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -432,8 +426,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -459,14 +451,10 @@ 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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.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.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -477,8 +465,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
|
||||
@@ -57,11 +57,10 @@ var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP"))
|
||||
var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME")
|
||||
var dockerApiVersion = strings.ToLower(os.Getenv("DOCKER_API_VERSION"))
|
||||
|
||||
var baseimagename = "frikky/shuffle"
|
||||
var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE")
|
||||
var executionCount int64
|
||||
|
||||
// var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME")
|
||||
var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME")
|
||||
|
||||
// var baseimagename = "registry.hub.docker.com/frikky/shuffle"
|
||||
var registryName = "registry.hub.docker.com"
|
||||
@@ -106,9 +105,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",
|
||||
}
|
||||
@@ -3726,7 +3723,7 @@ func main() {
|
||||
}
|
||||
|
||||
if baseimagename == "" {
|
||||
log.Printf("[DEBUG] Setting baseimagename")
|
||||
log.Printf("[DEBUG] Setting baseimagename to frikky/shuffle")
|
||||
baseimagename = "frikky/shuffle" // Dockerhub
|
||||
//baseimagename = "shuffle" // Github (ghcr.io)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user