Compare commits
26 Commits
58472e1707
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb590af8d7 | ||
|
|
1209e50c58 | ||
|
|
2fb8f5ec6a | ||
|
|
0a8b0b0120 | ||
|
|
1c55b0b562 | ||
|
|
c0917724f2 | ||
|
|
242ddecfa4 | ||
|
|
5a682a9618 | ||
|
|
3bdff103aa | ||
|
|
9d5786cda8 | ||
|
|
e63b3aa56f | ||
|
|
0be80ebb4a | ||
|
|
a4f3f5abf8 | ||
|
|
184a5b4e0f | ||
|
|
48a8d13580 | ||
|
|
d2c361de4a | ||
|
|
33da34151d | ||
|
|
283eb1de25 | ||
|
|
5c3c1a8761 | ||
|
|
41edea6176 | ||
|
|
42f52faeba | ||
|
|
4f0f075e32 | ||
|
|
c1009cca64 | ||
|
|
f7c1fa9d20 | ||
|
|
62f81936d5 | ||
|
|
99c079cdea |
@@ -1,23 +0,0 @@
|
|||||||
REACT_APP_AUTH_CALLBACK_URL=https://ota-admin.cec-euprd.fiskerinc.com
|
|
||||||
REACT_APP_AUTH_SERVICE_URL=https://gw.cec-prd.fiskerinc.com/compute_auth
|
|
||||||
REACT_APP_CERT_SERVICE_URL=https://gw.cec-prd.fiskerinc.com/certificate
|
|
||||||
REACT_APP_ENV=cec-euprd
|
|
||||||
REACT_APP_MAGNA_PROVIDER=Magna
|
|
||||||
REACT_APP_MAGNA_GROUP_ID=68273225-9da4-4fa7-aea5-38e16ec471fe
|
|
||||||
REACT_APP_OTA_SERVICE_URL=https://gw.cec-euprd.fiskerinc.com/ota_update
|
|
||||||
REACT_APP_SECURITY_DLL_URL=https://assets.fiskerdps.com/cloud-supplier/fisker_security_32.dll
|
|
||||||
REACT_APP_SECURITY_DLL_64_URL=https://assets.fiskerdps.com/cloud-supplier/fisker_security_64.dll
|
|
||||||
REACT_APP_SUPERSET_URL=https://superset.cec-euprd.fiskerinc.com
|
|
||||||
REACT_APP_ROLE_CREATE=e92f2b3e-1b80-42e5-9483-8ae648224dc6
|
|
||||||
REACT_APP_ROLE_READ_ONLY=ae5123e3-7a6a-4ca9-947b-a0f184f82cbd
|
|
||||||
REACT_APP_ROLE_DELETE=bfd1cccc-213a-4f31-b3d1-6e685976aec8
|
|
||||||
REACT_APP_ROLE_GENERATE_CERTIFICATE=9af2d8c0-c26d-4d6d-bbd1-ac53cbd37ebc
|
|
||||||
REACT_APP_ROLE_MANUFACTURE=3412e11a-a2d1-4355-be3e-ef9aa5065b69
|
|
||||||
REACT_APP_ROLE_SUPPLIER_APPROVER=a6c9805e-80b2-42b2-bfbb-9df52e5504d8
|
|
||||||
REACT_APP_ROLE_MANIFEST_MIGRATION=42798c8a-9fa7-4fb4-82c0-9582cabe364f
|
|
||||||
REACT_APP_ROLE_CAR_DIAGNOSTIC=2914e67f-fb85-4b78-b79d-656f4f37faa1
|
|
||||||
REACT_APP_ROLE_UPDATE_DEPLOY=e4af2c4c-6c5e-4784-9097-7c18e776d7b6
|
|
||||||
REACT_APP_ECCKEY_ENV=
|
|
||||||
REACT_APP_HOME_MAP_DEFAULT_LOCATION={"lat":49.8327,"lng":9.8816,"zoom":4.5}
|
|
||||||
REACT_APP_ENABLE_DEBUGMASK=1
|
|
||||||
REACT_APP_SHOW_AFTERSALES_EU_CERT_BUTTON=0
|
|
||||||
23
.env.cec-prd
23
.env.cec-prd
@@ -1,23 +0,0 @@
|
|||||||
REACT_APP_AUTH_CALLBACK_URL=https://ota-admin.cec-prd.fiskerinc.com
|
|
||||||
REACT_APP_AUTH_SERVICE_URL=https://gw.cec-prd.fiskerinc.com/compute_auth
|
|
||||||
REACT_APP_CERT_SERVICE_URL=https://gw.cec-prd.fiskerinc.com/certificate
|
|
||||||
REACT_APP_ENV=cec-prd
|
|
||||||
REACT_APP_MAGNA_PROVIDER=Magna
|
|
||||||
REACT_APP_MAGNA_GROUP_ID=68273225-9da4-4fa7-aea5-38e16ec471fe
|
|
||||||
REACT_APP_OTA_SERVICE_URL=https://gw.cec-prd.fiskerinc.com/ota_update
|
|
||||||
REACT_APP_SECURITY_DLL_URL=https://assets.fiskerdps.com/cloud-supplier/fisker_security_32.dll
|
|
||||||
REACT_APP_SECURITY_DLL_64_URL=https://assets.fiskerdps.com/cloud-supplier/fisker_security_64.dll
|
|
||||||
REACT_APP_SUPERSET_URL=https://superset.cec-prd.fiskerinc.com
|
|
||||||
REACT_APP_ROLE_CREATE=e92f2b3e-1b80-42e5-9483-8ae648224dc6
|
|
||||||
REACT_APP_ROLE_READ_ONLY=ae5123e3-7a6a-4ca9-947b-a0f184f82cbd
|
|
||||||
REACT_APP_ROLE_DELETE=bfd1cccc-213a-4f31-b3d1-6e685976aec8
|
|
||||||
REACT_APP_ROLE_GENERATE_CERTIFICATE=9af2d8c0-c26d-4d6d-bbd1-ac53cbd37ebc
|
|
||||||
REACT_APP_ROLE_MANUFACTURE=3412e11a-a2d1-4355-be3e-ef9aa5065b69
|
|
||||||
REACT_APP_ROLE_SUPPLIER_APPROVER=a6c9805e-80b2-42b2-bfbb-9df52e5504d8
|
|
||||||
REACT_APP_ROLE_MANIFEST_MIGRATION=42798c8a-9fa7-4fb4-82c0-9582cabe364f
|
|
||||||
REACT_APP_ROLE_CAR_DIAGNOSTIC=2914e67f-fb85-4b78-b79d-656f4f37faa1
|
|
||||||
REACT_APP_ROLE_UPDATE_DEPLOY=e4af2c4c-6c5e-4784-9097-7c18e776d7b6
|
|
||||||
REACT_APP_ECCKEY_ENV=
|
|
||||||
REACT_APP_HOME_MAP_DEFAULT_LOCATION={"lat":37.0902,"lng":-95.7129,"zoom":4.5}
|
|
||||||
REACT_APP_ENABLE_DEBUGMASK=1
|
|
||||||
REACT_APP_SHOW_AFTERSALES_EU_CERT_BUTTON=1
|
|
||||||
23
.env.dev
23
.env.dev
@@ -1,23 +0,0 @@
|
|||||||
REACT_APP_AUTH_CALLBACK_URL=https://dev-ota-admin.cloud.fiskerinc.com
|
|
||||||
REACT_APP_AUTH_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/compute_auth
|
|
||||||
REACT_APP_CERT_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/certificate
|
|
||||||
REACT_APP_ENV=dev
|
|
||||||
REACT_APP_MAGNA_PROVIDER=Magna
|
|
||||||
REACT_APP_MAGNA_GROUP_ID=68273225-9da4-4fa7-aea5-38e16ec471fe
|
|
||||||
REACT_APP_OTA_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/ota_update
|
|
||||||
REACT_APP_SECURITY_DLL_URL=https://assets.fiskerdps.com/cloud-supplier/fisker_security_32.dll
|
|
||||||
REACT_APP_SECURITY_DLL_64_URL=https://assets.fiskerdps.com/cloud-supplier/fisker_security_64.dll
|
|
||||||
REACT_APP_SUPERSET_URL=https://dev-superset.cloud.fiskerinc.com
|
|
||||||
REACT_APP_ROLE_CREATE=efcc3025-e2d8-4212-8227-805c7be39d2c
|
|
||||||
REACT_APP_ROLE_READ_ONLY=a729bbd4-2038-4649-9127-16782bb1e701
|
|
||||||
REACT_APP_ROLE_DELETE=8f78dce7-f5f9-4033-a10c-c9c7408bfcfe
|
|
||||||
REACT_APP_ROLE_GENERATE_CERTIFICATE=746f34b0-9ba0-4b5d-8d84-0256a9c8e390
|
|
||||||
REACT_APP_ROLE_MANUFACTURE=3412e11a-a2d1-4355-be3e-ef9aa5065b69
|
|
||||||
REACT_APP_ROLE_SUPPLIER_APPROVER=a6c9805e-80b2-42b2-bfbb-9df52e5504d8
|
|
||||||
REACT_APP_ROLE_MANIFEST_MIGRATION=42798c8a-9fa7-4fb4-82c0-9582cabe364f
|
|
||||||
REACT_APP_ROLE_CAR_DIAGNOSTIC=2914e67f-fb85-4b78-b79d-656f4f37faa1
|
|
||||||
REACT_APP_ROLE_UPDATE_DEPLOY=3590ec3f-1c05-428b-81a4-40b00baf83de
|
|
||||||
REACT_APP_ECCKEY_ENV=stage,prod
|
|
||||||
REACT_APP_HOME_MAP_DEFAULT_LOCATION={"lat":37.0902,"lng":-95.7129,"zoom":4.5}
|
|
||||||
REACT_APP_ENABLE_DEBUGMASK=1
|
|
||||||
REACT_APP_SHOW_AFTERSALES_EU_CERT_BUTTON=1
|
|
||||||
@@ -2,6 +2,12 @@ REACT_APP_AUTH_CALLBACK_URL=http://localhost:3000
|
|||||||
REACT_APP_AUTH_SERVICE_URL=http://localhost/compute_auth
|
REACT_APP_AUTH_SERVICE_URL=http://localhost/compute_auth
|
||||||
REACT_APP_CERT_SERVICE_URL=http://localhost/certificate
|
REACT_APP_CERT_SERVICE_URL=http://localhost/certificate
|
||||||
REACT_APP_ENV=local
|
REACT_APP_ENV=local
|
||||||
|
|
||||||
|
# Keycloak OIDC
|
||||||
|
REACT_APP_KEYCLOAK_ENABLED=true
|
||||||
|
REACT_APP_KEYCLOAK_URL=https://keycloak.mini.cloud.fiskerinc.com
|
||||||
|
REACT_APP_KEYCLOAK_REALM=compute-auth
|
||||||
|
REACT_APP_KEYCLOAK_CLIENT_ID=ota-portal
|
||||||
REACT_APP_MAGNA_PROVIDER=Magna
|
REACT_APP_MAGNA_PROVIDER=Magna
|
||||||
REACT_APP_MAGNA_GROUP_ID=68273225-9da4-4fa7-aea5-38e16ec471fe
|
REACT_APP_MAGNA_GROUP_ID=68273225-9da4-4fa7-aea5-38e16ec471fe
|
||||||
REACT_APP_OTA_SERVICE_URL=http://localhost/ota_update
|
REACT_APP_OTA_SERVICE_URL=http://localhost/ota_update
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
REACT_APP_AUTH_CALLBACK_URL=https://ota-admin.cloud.fiskerinc.com
|
REACT_APP_AUTH_CALLBACK_URL=https://ota-admin.mini.cloud.fiskerinc.com
|
||||||
REACT_APP_AUTH_SERVICE_URL=https://gw.cloud.fiskerinc.com/compute_auth
|
REACT_APP_AUTH_SERVICE_URL=https://gw.cloud.fiskerinc.com/compute_auth
|
||||||
REACT_APP_CERT_SERVICE_URL=https://gw.cloud.fiskerinc.com/certificate
|
REACT_APP_CERT_SERVICE_URL=https://gw.cloud.fiskerinc.com/certificate
|
||||||
REACT_APP_ENV=prd
|
REACT_APP_ENV=mini
|
||||||
|
|
||||||
|
# Keycloak OIDC (set to true to enable)
|
||||||
|
REACT_APP_KEYCLOAK_ENABLED=true
|
||||||
|
REACT_APP_KEYCLOAK_URL=https://keycloak.mini.cloud.fiskerinc.com
|
||||||
|
REACT_APP_KEYCLOAK_REALM=compute-auth
|
||||||
|
REACT_APP_KEYCLOAK_CLIENT_ID=ota-portal
|
||||||
|
|
||||||
REACT_APP_MAGNA_PROVIDER=Magna
|
REACT_APP_MAGNA_PROVIDER=Magna
|
||||||
REACT_APP_MAGNA_GROUP_ID=68273225-9da4-4fa7-aea5-38e16ec471fe
|
REACT_APP_MAGNA_GROUP_ID=68273225-9da4-4fa7-aea5-38e16ec471fe
|
||||||
REACT_APP_OTA_SERVICE_URL=https://gw.cloud.fiskerinc.com/ota_update
|
REACT_APP_OTA_SERVICE_URL=https://gw.cloud.fiskerinc.com/ota_update
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
REACT_APP_AUTH_CALLBACK_URL=http://localhost:3000
|
|
||||||
REACT_APP_AUTH_SERVICE_URL=http://localhost/compute_auth
|
|
||||||
REACT_APP_CERT_SERVICE_URL=http://localhost/certificate
|
|
||||||
REACT_APP_ENV=local
|
|
||||||
REACT_APP_MAGNA_PROVIDER=DISABLED
|
|
||||||
REACT_APP_MAGNA_GROUP_ID=DISABLED
|
|
||||||
REACT_APP_OTA_SERVICE_URL=http://localhost/ota_update
|
|
||||||
REACT_APP_SECURITY_DLL_URL=https://assets.fiskerdps.com/cloud-supplier/fisker_security_32.dll
|
|
||||||
REACT_APP_SECURITY_DLL_64_URL=https://assets.fiskerdps.com/cloud-supplier/fisker_security_64.dll
|
|
||||||
REACT_APP_SUPERSET_URL=https://dev-superset.cloud.fiskerinc.com
|
|
||||||
REACT_APP_ROLE_CREATE=efcc3025-e2d8-4212-8227-805c7be39d2c
|
|
||||||
REACT_APP_ROLE_READ_ONLY=a729bbd4-2038-4649-9127-16782bb1e701
|
|
||||||
REACT_APP_ROLE_DELETE=8f78dce7-f5f9-4033-a10c-c9c7408bfcfe
|
|
||||||
REACT_APP_ROLE_GENERATE_CERTIFICATE=746f34b0-9ba0-4b5d-8d84-0256a9c8e390
|
|
||||||
REACT_APP_ROLE_MANUFACTURE=3412e11a-a2d1-4355-be3e-ef9aa5065b69
|
|
||||||
REACT_APP_ROLE_SUPPLIER_APPROVER=a6c9805e-80b2-42b2-bfbb-9df52e5504d8
|
|
||||||
REACT_APP_ROLE_MANIFEST_MIGRATION=42798c8a-9fa7-4fb4-82c0-9582cabe364f
|
|
||||||
REACT_APP_ROLE_CAR_DIAGNOSTIC=2914e67f-fb85-4b78-b79d-656f4f37faa1
|
|
||||||
REACT_APP_ROLE_UPDATE_DEPLOY=3590ec3f-1c05-428b-81a4-40b00baf83de
|
|
||||||
REACT_APP_ECCKEY_ENV=dev,stage,prod
|
|
||||||
REACT_APP_HOME_MAP_DEFAULT_LOCATION={"lat":37.0902,"lng":-95.7129,"zoom":4.5}
|
|
||||||
REACT_APP_SHOW_AFTERSALES_EU_CERT_BUTTON=0
|
|
||||||
7
.github/CODEOWNERS
vendored
7
.github/CODEOWNERS
vendored
@@ -1,7 +0,0 @@
|
|||||||
# default codeowners
|
|
||||||
* @Fisker-Inc/cloud
|
|
||||||
|
|
||||||
# devops
|
|
||||||
.github @Fisker-Inc/devops @Fisker-Inc/cloud
|
|
||||||
k8s @Fisker-Inc/devops @Fisker-Inc/cloud
|
|
||||||
Dockerfile @Fisker-Inc/devops @Fisker-Inc/cloud
|
|
||||||
38
.github/workflows/blackduck.yml
vendored
38
.github/workflows/blackduck.yml
vendored
@@ -1,38 +0,0 @@
|
|||||||
name: Blackduck Intelligent Scan
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# run scans twice a month
|
|
||||||
- cron: '0 2 1,15 * *'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
blackduck:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
cache: npm
|
|
||||||
|
|
||||||
- run: npm install
|
|
||||||
- run: npm run build
|
|
||||||
|
|
||||||
- name: Run Synopsys Detect INTELLIGENT
|
|
||||||
run: |
|
|
||||||
bash <(curl -s -L https://detect.synopsys.com/detect9.sh) \
|
|
||||||
--blackduck.url=${{ secrets.BLACKDUCK_URL }} \
|
|
||||||
--blackduck.api.token=${{ secrets.BLACKDUCK_API_KEY }} \
|
|
||||||
--blackduck.trust.cert=true \
|
|
||||||
--detect.project.version.update=true \
|
|
||||||
--detect.project.name='ota-admin-portal' \
|
|
||||||
--detect.excluded.directories='node_modules' \
|
|
||||||
--detect.project.version.name=$GITHUB_REF_NAME \
|
|
||||||
--detect.blackduck.scan.mode="INTELLIGENT" \
|
|
||||||
--detect.detector.search.depth=3 \
|
|
||||||
--detect.detector.search.continue=true \
|
|
||||||
--detect.npm.include.dev.dependencies=false
|
|
||||||
# --detect.policy.check.fail.on.severities=ALL,NONE,UNSPECIFIED,TRIVIAL,MINOR,MAJOR,CRITICAL,BLOCKER - Use it if you want to fail the build on a certain severity type
|
|
||||||
# --detect.detector.search.continue=true - If true, the bom tool search will continue to look for nested bom tools of the same type to the maximum search depth
|
|
||||||
41
.github/workflows/blackduck_rapid.yml
vendored
41
.github/workflows/blackduck_rapid.yml
vendored
@@ -1,41 +0,0 @@
|
|||||||
name: Blackduck Rapid scan
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
blackduck:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
cache: npm
|
|
||||||
|
|
||||||
- run: npm install
|
|
||||||
- run: npm run build
|
|
||||||
|
|
||||||
- name: Run Synopsys Detect RAPID
|
|
||||||
run: |
|
|
||||||
bash <(curl -s -L https://detect.synopsys.com/detect9.sh) \
|
|
||||||
--blackduck.url=${{ secrets.BLACKDUCK_URL }} \
|
|
||||||
--blackduck.api.token=${{ secrets.BLACKDUCK_API_KEY }} \
|
|
||||||
--blackduck.trust.cert=true \
|
|
||||||
--detect.project.version.update=true \
|
|
||||||
--detect.project.name='ota-admin-portal' \
|
|
||||||
--detect.excluded.directories='node_modules' \
|
|
||||||
--detect.project.version.name=$GITHUB_REF_NAME \
|
|
||||||
--detect.blackduck.scan.mode="RAPID" \
|
|
||||||
--detect.detector.search.depth=3 \
|
|
||||||
--detect.detector.search.continue=true \
|
|
||||||
--detect.npm.include.dev.dependencies=false
|
|
||||||
# --detect.detector.search.continue=true - If true, the bom tool search will continue to look for nested bom tools of the same type to the maximum search depth
|
|
||||||
135
.github/workflows/deploy-on-demand-v2.yml
vendored
135
.github/workflows/deploy-on-demand-v2.yml
vendored
@@ -1,135 +0,0 @@
|
|||||||
name: OTA Admin Portal v2 Deploy - On Demand
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
environment:
|
|
||||||
description: "Environment"
|
|
||||||
required: true
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- dev
|
|
||||||
- preprod
|
|
||||||
- cec-prd
|
|
||||||
- cec-euprd
|
|
||||||
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
||||||
SLACK_CHANNEL: "#cloud-builds"
|
|
||||||
SLACK_FOOTER: ""
|
|
||||||
SLACK_USERNAME: GitHub Actions
|
|
||||||
SLACK_ICON: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
|
|
||||||
JFROG_NPMRC: ${{ secrets.JFROG_NPMRC }}
|
|
||||||
TAG: ${{ github.sha }}
|
|
||||||
PROJECT: ota-admin-portal-v2
|
|
||||||
REGISTRY: fiskercloud.azurecr.io
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Azure Login
|
|
||||||
uses: azure/login@v2
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID_DEV }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID_DEV }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID_DEV }}
|
|
||||||
|
|
||||||
- name: ACR Login
|
|
||||||
run: |
|
|
||||||
az acr login --name ${{ env.REGISTRY }}
|
|
||||||
|
|
||||||
- name: JFrog Auth
|
|
||||||
run: echo ${JFROG_NPMRC} | base64 -d > .npmrc
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
build-args: ENVIRONMENT=${{ inputs.environment }}
|
|
||||||
push: true
|
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.PROJECT }}:${{ env.TAG }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
||||||
- name: Notify if failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to build ${{ env.PROJECT }} in ${{ inputs.environment }}! :this-is-fine:"
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
needs: build
|
|
||||||
runs-on: arc-azure-${{ inputs.environment }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Notify deploy
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Deploying ${{ env.PROJECT }} to ${{ inputs.environment }}... :partydeploy:"
|
|
||||||
|
|
||||||
- name: Set env
|
|
||||||
run: |
|
|
||||||
case ${{ inputs.environment }} in
|
|
||||||
dev)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_DEV }};;
|
|
||||||
preprod)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_PREPROD }};;
|
|
||||||
cec-prd)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_CEC_PRD }};;
|
|
||||||
cec-euprd)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_CEC_EUPRD }};;
|
|
||||||
*)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_DEV }};;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "KUBECONFIG=${KUBECONFIG}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Deploy to env
|
|
||||||
id: deploy
|
|
||||||
uses: koslib/helm-eks-action@v1.28.0
|
|
||||||
env:
|
|
||||||
KUBE_CONFIG_DATA: ${{ env.KUBECONFIG }}
|
|
||||||
with:
|
|
||||||
command: |
|
|
||||||
helm upgrade \
|
|
||||||
--atomic \
|
|
||||||
--create-namespace \
|
|
||||||
--namespace default \
|
|
||||||
--set image.registry=$REGISTRY \
|
|
||||||
--set image.name=$PROJECT \
|
|
||||||
--set image.tag=$TAG \
|
|
||||||
--wait -i -f k8s/values-${{ inputs.environment }}.yaml $PROJECT k8s/
|
|
||||||
|
|
||||||
- name: Deploy Response
|
|
||||||
run: echo "Response was ${{ steps.deploy.outputs.response }}"
|
|
||||||
|
|
||||||
- name: Notify deploy failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to deploy ${{ env.PROJECT }} on ${{ inputs.environment }}! :this-is-fine:"
|
|
||||||
|
|
||||||
- name: Notify deploy success
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Successfully deployed ${{ env.PROJECT }} to ${{ inputs.environment }}! :gopher_party:"
|
|
||||||
131
.github/workflows/deploy-on-demand.yml
vendored
131
.github/workflows/deploy-on-demand.yml
vendored
@@ -1,131 +0,0 @@
|
|||||||
name: OTA Portal Deploy - On Demand
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
environment:
|
|
||||||
description: "Environment"
|
|
||||||
required: true
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- dev
|
|
||||||
- preprod
|
|
||||||
- cec-prd
|
|
||||||
- cec-euprd
|
|
||||||
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
||||||
SLACK_CHANNEL: "#cloud-builds"
|
|
||||||
SLACK_FOOTER: ""
|
|
||||||
SLACK_USERNAME: GitHub Actions
|
|
||||||
SLACK_ICON: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
|
|
||||||
TAG: ${{ github.sha }}
|
|
||||||
PROJECT: ota-admin-portal
|
|
||||||
REGISTRY: fiskercloud.azurecr.io
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Azure Login
|
|
||||||
uses: azure/login@v2
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID_DEV }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID_DEV }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID_DEV }}
|
|
||||||
|
|
||||||
- name: ACR Login
|
|
||||||
run: |
|
|
||||||
az acr login --name ${{ env.REGISTRY }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
build-args: ENVIRONMENT=${{ inputs.environment }}
|
|
||||||
push: true
|
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.PROJECT }}:${{ env.TAG }}-${{ inputs.environment }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
||||||
- name: Notify if failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to build ${{ env.PROJECT }} in ${{ inputs.environment }}! :this-is-fine:"
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
needs: build
|
|
||||||
runs-on: arc-azure-${{ inputs.environment }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Notify deploy
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Deploying ${{ env.PROJECT }} to ${{ inputs.environment }}... :partydeploy:"
|
|
||||||
|
|
||||||
- name: Set env
|
|
||||||
run: |
|
|
||||||
case ${{ inputs.environment }} in
|
|
||||||
dev)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_DEV }};;
|
|
||||||
preprod)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_PREPROD }};;
|
|
||||||
cec-prd)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_CEC_PRD }};;
|
|
||||||
cec-euprd)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_CEC_EUPRD }};;
|
|
||||||
*)
|
|
||||||
KUBECONFIG=${{ secrets.KUBECONFIG_AZURE_DEV }};;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "KUBECONFIG=${KUBECONFIG}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Deploy to env
|
|
||||||
id: deploy
|
|
||||||
uses: koslib/helm-eks-action@v1.28.0
|
|
||||||
env:
|
|
||||||
KUBE_CONFIG_DATA: ${{ env.KUBECONFIG }}
|
|
||||||
with:
|
|
||||||
command: |
|
|
||||||
helm upgrade \
|
|
||||||
--atomic \
|
|
||||||
--create-namespace \
|
|
||||||
--namespace default \
|
|
||||||
--set image.registry=$REGISTRY \
|
|
||||||
--set image.name=$PROJECT \
|
|
||||||
--set image.tag=$TAG-${{ inputs.environment }} \
|
|
||||||
--wait -i -f k8s/values-${{ inputs.environment }}.yaml $PROJECT k8s/
|
|
||||||
|
|
||||||
- name: Deploy Response
|
|
||||||
run: echo "Response was ${{ steps.deploy.outputs.response }}"
|
|
||||||
|
|
||||||
- name: Notify deploy failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to deploy ${{ env.PROJECT }} to ${{ inputs.environment }}! :this-is-fine:"
|
|
||||||
|
|
||||||
- name: Notify deploy success
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Successfully deployed ${{ env.PROJECT }} to ${{ inputs.environment }}! :gopher_party:"
|
|
||||||
371
.github/workflows/deploy.yml
vendored
371
.github/workflows/deploy.yml
vendored
@@ -1,371 +0,0 @@
|
|||||||
name: OTA Portal Deploy
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
||||||
SLACK_CHANNEL: "#cloud-builds"
|
|
||||||
SLACK_FOOTER: ""
|
|
||||||
SLACK_USERNAME: GitHub Actions
|
|
||||||
SLACK_ICON: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
|
|
||||||
TAG: ${{ github.sha }}
|
|
||||||
PROJECT: ota-admin-portal
|
|
||||||
REGISTRY: fiskercloud.azurecr.io
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-dev:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Azure Login
|
|
||||||
uses: azure/login@v2
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID_DEV }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID_DEV }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID_DEV }}
|
|
||||||
|
|
||||||
- name: ACR Login
|
|
||||||
run: |
|
|
||||||
az acr login --name ${{ env.REGISTRY }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build and push DEV
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
build-args: ENVIRONMENT=dev
|
|
||||||
push: true
|
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.PROJECT }}:${{ env.TAG }}-dev
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
||||||
- name: Notify if failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to build ${{ env.PROJECT }} dev! :this-is-fine:"
|
|
||||||
|
|
||||||
deploy-dev:
|
|
||||||
needs: [build-dev]
|
|
||||||
runs-on: [ arc-azure-dev ]
|
|
||||||
environment: dev
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Notify deploy
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Deploying ${{ env.PROJECT }} to dev... :partydeploy:"
|
|
||||||
|
|
||||||
- name: Deploy to dev
|
|
||||||
id: deploy
|
|
||||||
uses: koslib/helm-eks-action@v1.28.0
|
|
||||||
env:
|
|
||||||
KUBE_CONFIG_DATA: ${{ secrets.KUBECONFIG_AZURE_DEV }}
|
|
||||||
with:
|
|
||||||
command: |
|
|
||||||
helm upgrade \
|
|
||||||
--atomic \
|
|
||||||
--create-namespace \
|
|
||||||
--namespace default \
|
|
||||||
--set image.registry=$REGISTRY \
|
|
||||||
--set image.name=$PROJECT \
|
|
||||||
--set image.tag=$TAG-dev \
|
|
||||||
--wait -i -f k8s/values-dev.yaml $PROJECT k8s/
|
|
||||||
|
|
||||||
- name: Deploy Response
|
|
||||||
run: echo "Response was ${{ steps.deploy.outputs.response }}"
|
|
||||||
|
|
||||||
- name: Notify deploy failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to deploy ${{ env.PROJECT }} on dev! :this-is-fine:"
|
|
||||||
|
|
||||||
- name: Notify deploy success
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Successfully deployed ${{ env.PROJECT }} to dev! :gopher_party:"
|
|
||||||
|
|
||||||
build-preprod:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [deploy-dev]
|
|
||||||
steps:
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Azure Login
|
|
||||||
uses: azure/login@v2
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID_DEV }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID_DEV }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID_DEV }}
|
|
||||||
|
|
||||||
- name: ACR Login
|
|
||||||
run: |
|
|
||||||
az acr login --name ${{ env.REGISTRY }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build and push PREPROD
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
build-args: ENVIRONMENT=prd
|
|
||||||
push: true
|
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.PROJECT }}:${{ env.TAG }}-prd
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
||||||
- name: Notify if failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to build ${{ env.PROJECT }} preprod! :this-is-fine:"
|
|
||||||
|
|
||||||
deploy-preprod:
|
|
||||||
needs: [deploy-dev, build-preprod]
|
|
||||||
runs-on: [ arc-azure-preprod ]
|
|
||||||
environment: preprod
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Notify deploy
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Deploying ${{ env.PROJECT }} to preprod... :partydeploy:"
|
|
||||||
|
|
||||||
- name: Deploy to preprod
|
|
||||||
id: deploy
|
|
||||||
uses: koslib/helm-eks-action@v1.28.0
|
|
||||||
env:
|
|
||||||
KUBE_CONFIG_DATA: ${{ secrets.KUBECONFIG_AZURE_PREPROD }}
|
|
||||||
with:
|
|
||||||
command: |
|
|
||||||
helm upgrade \
|
|
||||||
--atomic \
|
|
||||||
--create-namespace \
|
|
||||||
--namespace default \
|
|
||||||
--set image.registry=$REGISTRY \
|
|
||||||
--set image.name=$PROJECT \
|
|
||||||
--set image.tag=$TAG-prd \
|
|
||||||
--wait -i -f k8s/values-prd.yaml $PROJECT k8s/
|
|
||||||
|
|
||||||
- name: Deploy Response
|
|
||||||
run: echo "Response was ${{ steps.deploy.outputs.response }}"
|
|
||||||
|
|
||||||
- name: Notify deploy failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to deploy ${{ env.PROJECT }} on preprod! :this-is-fine:"
|
|
||||||
|
|
||||||
- name: Notify deploy success
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Successfully deployed ${{ env.PROJECT }} to preprod! :gopher_party:"
|
|
||||||
|
|
||||||
build-cec-prd:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [deploy-dev, deploy-preprod]
|
|
||||||
steps:
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Azure Login
|
|
||||||
uses: azure/login@v2
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID_DEV }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID_DEV }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID_DEV }}
|
|
||||||
|
|
||||||
- name: ACR Login
|
|
||||||
run: |
|
|
||||||
az acr login --name ${{ env.REGISTRY }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build and push CEC-PRD
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
build-args: ENVIRONMENT=cec-prd
|
|
||||||
push: true
|
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.PROJECT }}:${{ env.TAG }}-cec-prd
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
||||||
- name: Notify if failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to build ${{ env.PROJECT }} cec-prd! :this-is-fine:"
|
|
||||||
|
|
||||||
deploy-cec-prd:
|
|
||||||
needs: [deploy-dev, deploy-preprod, build-cec-prd]
|
|
||||||
runs-on: [ arc-azure-cec-prd ]
|
|
||||||
environment: prd
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Notify deploy
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Deploying ${{ env.PROJECT }} to cec-prd... :partydeploy:"
|
|
||||||
|
|
||||||
- name: Deploy to cec-prd
|
|
||||||
id: deploy
|
|
||||||
uses: koslib/helm-eks-action@v1.28.0
|
|
||||||
env:
|
|
||||||
KUBE_CONFIG_DATA: ${{ secrets.KUBECONFIG_AZURE_CEC_PRD }}
|
|
||||||
with:
|
|
||||||
command: |
|
|
||||||
helm upgrade \
|
|
||||||
--atomic \
|
|
||||||
--create-namespace \
|
|
||||||
--namespace default \
|
|
||||||
--set image.registry=$REGISTRY \
|
|
||||||
--set image.name=$PROJECT \
|
|
||||||
--set image.tag=$TAG-cec-prd \
|
|
||||||
--wait -i -f k8s/values-cec-prd.yaml $PROJECT k8s/
|
|
||||||
|
|
||||||
- name: Deploy Response
|
|
||||||
run: echo "Response was ${{ steps.deploy.outputs.response }}"
|
|
||||||
|
|
||||||
- name: Notify deploy failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to deploy ${{ env.PROJECT }} on cec-prd! :this-is-fine:"
|
|
||||||
|
|
||||||
- name: Notify deploy success
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Successfully deployed ${{ env.PROJECT }} to cec-prd! :gopher_party:"
|
|
||||||
|
|
||||||
build-cec-euprd:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [deploy-dev, deploy-preprod]
|
|
||||||
steps:
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Azure Login
|
|
||||||
uses: azure/login@v2
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID_DEV }}
|
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID_DEV }}
|
|
||||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID_DEV }}
|
|
||||||
|
|
||||||
- name: ACR Login
|
|
||||||
run: |
|
|
||||||
az acr login --name ${{ env.REGISTRY }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Build and push CEC-EUPRD
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
build-args: ENVIRONMENT=cec-euprd
|
|
||||||
push: true
|
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.PROJECT }}:${{ env.TAG }}-cec-euprd
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
||||||
- name: Notify if failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to build ${{ env.PROJECT }} cec-euprd! :this-is-fine:"
|
|
||||||
|
|
||||||
deploy-cec-euprd:
|
|
||||||
needs: [deploy-dev, deploy-preprod, build-cec-euprd]
|
|
||||||
runs-on: [ arc-azure-cec-euprd ]
|
|
||||||
environment: prd
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Notify deploy
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Deploying ${{ env.PROJECT }} to cec-euprd... :partydeploy:"
|
|
||||||
|
|
||||||
- name: Deploy to cec-euprd
|
|
||||||
id: deploy
|
|
||||||
uses: koslib/helm-eks-action@v1.28.0
|
|
||||||
env:
|
|
||||||
KUBE_CONFIG_DATA: ${{ secrets.KUBECONFIG_AZURE_CEC_EUPRD }}
|
|
||||||
with:
|
|
||||||
command: |
|
|
||||||
helm upgrade \
|
|
||||||
--atomic \
|
|
||||||
--create-namespace \
|
|
||||||
--namespace default \
|
|
||||||
--set image.registry=$REGISTRY \
|
|
||||||
--set image.name=$PROJECT \
|
|
||||||
--set image.tag=$TAG-cec-euprd \
|
|
||||||
--wait -i -f k8s/values-cec-euprd.yaml $PROJECT k8s/
|
|
||||||
|
|
||||||
- name: Deploy Response
|
|
||||||
run: echo "Response was ${{ steps.deploy.outputs.response }}"
|
|
||||||
|
|
||||||
- name: Notify deploy failure
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
SLACK_COLOR: ${{ job.status }}
|
|
||||||
SLACK_MESSAGE: "Failed to deploy ${{ env.PROJECT }} on cec-euprd! :this-is-fine:"
|
|
||||||
|
|
||||||
- name: Notify deploy success
|
|
||||||
uses: rtCamp/action-slack-notify@v2
|
|
||||||
env:
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
SLACK_MESSAGE: "Successfully deployed ${{ env.PROJECT }} to cec-euprd! :gopher_party:"
|
|
||||||
12
.github/workflows/pr.yml
vendored
12
.github/workflows/pr.yml
vendored
@@ -1,12 +0,0 @@
|
|||||||
name: Pull Request Jira Check
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
types: [opened, edited, synchronize, reopened]
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
prcheck:
|
|
||||||
uses: Fisker-Inc/github-actions/.github/workflows/pr.yml@v1.0.16
|
|
||||||
35
.github/workflows/test.yml
vendored
35
.github/workflows/test.yml
vendored
@@ -1,35 +0,0 @@
|
|||||||
name: Node.js CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "20"
|
|
||||||
cache: "npm"
|
|
||||||
- run: npm install
|
|
||||||
- run: npm run build --if-present
|
|
||||||
- run: npm test -- --coverage --coverageDirectory='coverage' --watchAll=false
|
|
||||||
env:
|
|
||||||
CI: true
|
|
||||||
|
|
||||||
- name: SonarCloud Scan
|
|
||||||
uses: sonarsource/sonarcloud-github-action@master
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
||||||
30
.vscode/launch.json
vendored
30
.vscode/launch.json
vendored
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "pwa-node",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Launch Program",
|
|
||||||
"skipFiles": [
|
|
||||||
"<node_internals>/**"
|
|
||||||
],
|
|
||||||
"program": "${file}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Debug CRA Tests",
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
|
|
||||||
"args": ["test", "--runInBand", "--no-cache", "--watchAll=false"],
|
|
||||||
"cwd": "${workspaceRoot}",
|
|
||||||
"protocol": "inspector",
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"internalConsoleOptions": "neverOpen",
|
|
||||||
"env": { "CI": "true" },
|
|
||||||
"disableOptimisticBPs": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
// spacing
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
"editor.insertSpaces": true,
|
|
||||||
"editor.detectIndentation": false
|
|
||||||
}
|
|
||||||
83
README.md
83
README.md
@@ -1,69 +1,42 @@
|
|||||||
# Fisker Admin Portal
|
# OTA Admin Portal
|
||||||
|
|
||||||
Front-end web application for administrating services.
|
Front-end web application for OTA administration.
|
||||||
|
|
||||||
# Setup
|
## Setup
|
||||||
|
|
||||||
Running locally
|
```bash
|
||||||
|
npm install
|
||||||
|
npm start
|
||||||
|
# Access at http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
1. Install Node 16
|
## Build & Deploy
|
||||||
2. Run `npm install`
|
|
||||||
3. Copy .env.template to .env and edit the service urls for authentication and api services
|
|
||||||
4. Run `./run.sh` from the terminal
|
|
||||||
5. Access portal at localhost:3000
|
|
||||||
|
|
||||||
Running Docker container
|
```bash
|
||||||
|
# Build and push Docker image
|
||||||
|
docker build --build-arg ENVIRONMENT=mini -t gitea.mini.cloud.fiskerinc.com/admin/ota-admin-portal:latest .
|
||||||
|
docker login gitea.mini.cloud.fiskerinc.com -u admin
|
||||||
|
docker push gitea.mini.cloud.fiskerinc.com/admin/ota-admin-portal:latest
|
||||||
|
```
|
||||||
|
|
||||||
1. Copy .env.template to .env and edit the service urls for authentication and api services
|
## Environment
|
||||||
2. Build the image `docker build --build-arg ENVIRONMENT=local -t fiskerinc/portal .`
|
|
||||||
3. Start the container `docker run -p 3000:80 fiskerinc/portal`
|
|
||||||
4. Access portal at localhost:3000
|
|
||||||
|
|
||||||
## Available Scripts
|
- `.env.local` - Local development
|
||||||
|
- `.env.mini` - Mini cluster (Mac Mini)
|
||||||
|
|
||||||
In the project directory, you can run:
|
## Deployment
|
||||||
|
|
||||||
### `npm start`
|
Deployed via ArgoCD from `k8s/` helm chart with `values-mini.yaml`.
|
||||||
|
|
||||||
Runs the app in the development mode.\
|
URL: https://ota-admin.mini.cloud.fiskerinc.com
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|
||||||
|
|
||||||
The page will reload if you make edits.\
|
## Mini Cluster Login (Keycloak)
|
||||||
You will also see any lint errors in the console.
|
|
||||||
|
|
||||||
### `npm test`
|
The mini cluster uses Keycloak instead of Cognito. Test users for the `compute-auth` realm:
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.\
|
| User | Password | Role |
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|------|----------|------|
|
||||||
|
| admin | Admin123! | Full access |
|
||||||
|
| operator | Operator123! | Operator access |
|
||||||
|
|
||||||
### `npm run build`
|
Keycloak admin console: https://keycloak.mini.cloud.fiskerinc.com (admin / admin123)
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.\
|
|
||||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.\
|
|
||||||
Your app is ready to be deployed!
|
|
||||||
|
|
||||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
|
||||||
|
|
||||||
### `npm run eject`
|
|
||||||
|
|
||||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
|
||||||
|
|
||||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
|
||||||
|
|
||||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
|
||||||
|
|
||||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
|
||||||
|
|
||||||
### Advanced Configuration
|
|
||||||
|
|
||||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
|
||||||
|
|
||||||
### Deployment
|
|
||||||
|
|
||||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
|
||||||
|
|
||||||
### `npm run build` fails to minify
|
|
||||||
|
|
||||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
|
||||||
|
|||||||
12
devbox.json
Normal file
12
devbox.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/jetify-com/devbox/main/.schema/devbox.schema.json",
|
||||||
|
"packages": [
|
||||||
|
"nodejs_18",
|
||||||
|
"docker"
|
||||||
|
],
|
||||||
|
"shell": {
|
||||||
|
"init_hook": [
|
||||||
|
"echo 'OTA Admin Portal dev environment ready'"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- name: {{ .Chart.Name }}
|
- name: {{ .Chart.Name }}
|
||||||
image: "{{ .Values.image.registry }}/{{ .Values.image.name }}:{{ .Values.image.tag}}"
|
image: "{{ .Values.image.registry }}/{{ .Values.image.name }}:{{ .Values.image.tag}}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy | default "Always" }}
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ apiVersion: networking.k8s.io/v1
|
|||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
cert-manager.io/cluster-issuer: {{ .Values.ingress.clusterIssuer | default "letsencrypt-prod" }}
|
||||||
labels:
|
labels:
|
||||||
app: {{ .Chart.Name }}
|
app: {{ .Chart.Name }}
|
||||||
name: {{ .Chart.Name }}
|
name: {{ .Chart.Name }}
|
||||||
spec:
|
spec:
|
||||||
ingressClassName: nginx
|
ingressClassName: {{ .Values.ingress.className | default "nginx" }}
|
||||||
rules:
|
rules:
|
||||||
- host: {{ .Values.ingress.hostname }}
|
- host: {{ .Values.ingress.hostname }}
|
||||||
http:
|
http:
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
ingress:
|
|
||||||
hostname: ota-admin.cec-euprd.fiskerinc.com
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
#cpu: 250m
|
|
||||||
memory: 256Mi
|
|
||||||
|
|
||||||
replicas: 3
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
ingress:
|
|
||||||
hostname: ota-admin.cec-prd.fiskerinc.com
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
#cpu: 250m
|
|
||||||
memory: 256Mi
|
|
||||||
|
|
||||||
replicas: 3
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
ingress:
|
|
||||||
hostname: dev-ota-admin.cloud.fiskerinc.com
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
#cpu: 250m
|
|
||||||
memory: 256Mi
|
|
||||||
|
|
||||||
replicas: 1
|
|
||||||
18
k8s/values-mini.yaml
Normal file
18
k8s/values-mini.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
ingress:
|
||||||
|
hostname: ota-admin.mini.cloud.fiskerinc.com
|
||||||
|
className: traefik
|
||||||
|
|
||||||
|
image:
|
||||||
|
registry: localhost:32000
|
||||||
|
name: ota-admin-portal
|
||||||
|
tag: preprod4
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
memory: 256Mi
|
||||||
|
|
||||||
|
replicas: 1
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
ingress:
|
|
||||||
hostname: ota-admin.cloud.fiskerinc.com
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
#cpu: 250m
|
|
||||||
memory: 256Mi
|
|
||||||
|
|
||||||
replicas: 3
|
|
||||||
106
package-lock.json
generated
106
package-lock.json
generated
@@ -34,10 +34,12 @@
|
|||||||
"leaflet": "^1.8.0",
|
"leaflet": "^1.8.0",
|
||||||
"material-ui-dropzone": "^3.5.0",
|
"material-ui-dropzone": "^3.5.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"oidc-client-ts": "^3.4.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-leaflet": "^3.2.5",
|
"react-leaflet": "^3.2.5",
|
||||||
"react-leaflet-cluster": "^1.0.3",
|
"react-leaflet-cluster": "^1.0.3",
|
||||||
|
"react-oidc-context": "^3.3.0",
|
||||||
"react-router-dom": "^5.3.0",
|
"react-router-dom": "^5.3.0",
|
||||||
"react-router-hash-link": "^2.4.3",
|
"react-router-hash-link": "^2.4.3",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
@@ -3952,6 +3954,21 @@
|
|||||||
"string.prototype.matchall": "^4.0.6"
|
"string.prototype.matchall": "^4.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@surma/rollup-plugin-off-main-thread/node_modules/ejs": {
|
||||||
|
"version": "3.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
||||||
|
"integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"jake": "^10.8.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"ejs": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
|
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
|
||||||
"version": "5.4.0",
|
"version": "5.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz",
|
||||||
@@ -7361,20 +7378,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||||
},
|
},
|
||||||
"node_modules/ejs": {
|
|
||||||
"version": "3.1.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
|
||||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
|
||||||
"dependencies": {
|
|
||||||
"jake": "^10.8.5"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"ejs": "bin/cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.567",
|
"version": "1.4.567",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz",
|
||||||
@@ -12215,6 +12218,27 @@
|
|||||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/oidc-client-ts": {
|
||||||
|
"version": "3.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.4.1.tgz",
|
||||||
|
"integrity": "sha512-jNdst/U28Iasukx/L5MP6b274Vr7ftQs6qAhPBCvz6Wt5rPCA+Q/tUmCzfCHHWweWw5szeMy2Gfrm1rITwUKrw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"jwt-decode": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/oidc-client-ts/node_modules/jwt-decode": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/on-finished": {
|
"node_modules/on-finished": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
@@ -14170,6 +14194,19 @@
|
|||||||
"react-leaflet": "^3.0.2"
|
"react-leaflet": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-oidc-context": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-oidc-context/-/react-oidc-context-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-302T/ma4AOVAxrHdYctDSKXjCq9KNHT564XEO2yOPxRfxEP58xa4nz+GQinNl8x7CnEXECSM5JEjQJk3Cr5BvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"oidc-client-ts": "^3.1.0",
|
||||||
|
"react": ">=16.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
|
||||||
@@ -19970,10 +20007,20 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
||||||
"integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==",
|
"integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ejs": "^3.1.6",
|
"ejs": "3.1.9",
|
||||||
"json5": "^2.2.0",
|
"json5": "^2.2.0",
|
||||||
"magic-string": "^0.25.0",
|
"magic-string": "^0.25.0",
|
||||||
"string.prototype.matchall": "^4.0.6"
|
"string.prototype.matchall": "^4.0.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ejs": {
|
||||||
|
"version": "3.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
||||||
|
"integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==",
|
||||||
|
"requires": {
|
||||||
|
"jake": "^10.8.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@svgr/babel-plugin-add-jsx-attribute": {
|
"@svgr/babel-plugin-add-jsx-attribute": {
|
||||||
@@ -22470,14 +22517,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||||
},
|
},
|
||||||
"ejs": {
|
|
||||||
"version": "3.1.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
|
||||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
|
||||||
"requires": {
|
|
||||||
"jake": "^10.8.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.4.567",
|
"version": "1.4.567",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz",
|
||||||
@@ -26023,6 +26062,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
||||||
},
|
},
|
||||||
|
"oidc-client-ts": {
|
||||||
|
"version": "3.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.4.1.tgz",
|
||||||
|
"integrity": "sha512-jNdst/U28Iasukx/L5MP6b274Vr7ftQs6qAhPBCvz6Wt5rPCA+Q/tUmCzfCHHWweWw5szeMy2Gfrm1rITwUKrw==",
|
||||||
|
"requires": {
|
||||||
|
"jwt-decode": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jwt-decode": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"on-finished": {
|
"on-finished": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
@@ -27253,6 +27307,12 @@
|
|||||||
"leaflet.markercluster": "^1.4.1"
|
"leaflet.markercluster": "^1.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-oidc-context": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-oidc-context/-/react-oidc-context-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-302T/ma4AOVAxrHdYctDSKXjCq9KNHT564XEO2yOPxRfxEP58xa4nz+GQinNl8x7CnEXECSM5JEjQJk3Cr5BvA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"react-refresh": {
|
"react-refresh": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -29,10 +29,12 @@
|
|||||||
"leaflet": "^1.8.0",
|
"leaflet": "^1.8.0",
|
||||||
"material-ui-dropzone": "^3.5.0",
|
"material-ui-dropzone": "^3.5.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"oidc-client-ts": "^3.4.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-leaflet": "^3.2.5",
|
"react-leaflet": "^3.2.5",
|
||||||
"react-leaflet-cluster": "^1.0.3",
|
"react-leaflet-cluster": "^1.0.3",
|
||||||
|
"react-oidc-context": "^3.3.0",
|
||||||
"react-router-dom": "^5.3.0",
|
"react-router-dom": "^5.3.0",
|
||||||
"react-router-hash-link": "^2.4.3",
|
"react-router-hash-link": "^2.4.3",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
@@ -48,17 +50,11 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "env-cmd -f .env.local react-scripts start",
|
"start": "env-cmd -f .env.local react-scripts start",
|
||||||
"build": "env-cmd -f .env.local react-scripts build",
|
"build": "env-cmd -f .env.mini react-scripts build",
|
||||||
"build:dev": "env-cmd -f .env.dev react-scripts build",
|
"build:mini": "env-cmd -f .env.mini react-scripts build",
|
||||||
"build:stg": "env-cmd -f .env.stg react-scripts build",
|
|
||||||
"build:prd": "env-cmd -f .env.prd react-scripts build",
|
|
||||||
"build:cec-prd": "env-cmd -f .env.cec-prd react-scripts build",
|
|
||||||
"build:cec-euprd": "env-cmd -f .env.cec-euprd react-scripts build",
|
|
||||||
"build:local": "env-cmd -f .env.local react-scripts build",
|
"build:local": "env-cmd -f .env.local react-scripts build",
|
||||||
"test": "env-cmd -f .env.local react-scripts test --no-cache",
|
"test": "env-cmd -f .env.local react-scripts test --no-cache",
|
||||||
"test:debug": "react-scripts --inspect-brk test --runInBand --no-cache",
|
"test:coverage": "npm test -- --coverage --watchAll=false --no-cache"
|
||||||
"test:coverage": "npm test -- --coverage --watchAll=false --no-cache",
|
|
||||||
"eject": "react-scripts eject"
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
sonar.projectKey=Fisker-Inc_ota-admin-portal
|
|
||||||
sonar.organization=fisker-inc
|
|
||||||
sonar.sourceEncoding=UTF-8
|
|
||||||
sonar.javascript.coveragePlugin=lcov
|
|
||||||
sonar.javascript.lcov.reportPaths=coverage/lcov.info
|
|
||||||
sonar.sources=src
|
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
|
import { AuthProvider } from "react-oidc-context";
|
||||||
import { UserProvider } from "../Contexts/UserContext";
|
import { UserProvider } from "../Contexts/UserContext";
|
||||||
import { StatusProvider } from "../Contexts/StatusContext";
|
import { StatusProvider } from "../Contexts/StatusContext";
|
||||||
import { CssBaseline } from "@material-ui/core";
|
import { CssBaseline } from "@material-ui/core";
|
||||||
import MenuDrawer from "../Layouts/MenuDrawer";
|
import MenuDrawer from "../Layouts/MenuDrawer";
|
||||||
import SiteRoutes from "../Routes/SiteRoutes";
|
import SiteRoutes from "../Routes/SiteRoutes";
|
||||||
import { } from "../../services/monitoring";
|
import { } from "../../services/monitoring";
|
||||||
|
import { keycloakConfig, isKeycloakEnabled } from "../../services/keycloak";
|
||||||
|
|
||||||
function App() {
|
function AppContent() {
|
||||||
return (
|
return (
|
||||||
<StatusProvider>
|
<StatusProvider>
|
||||||
<UserProvider>
|
<UserProvider>
|
||||||
@@ -22,4 +24,25 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onSigninCallback = () => {
|
||||||
|
// Remove OIDC params from URL after signin
|
||||||
|
window.history.replaceState({}, document.title, window.location.pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
// Only wrap with AuthProvider if Keycloak is enabled
|
||||||
|
if (isKeycloakEnabled()) {
|
||||||
|
return (
|
||||||
|
<AuthProvider
|
||||||
|
{...keycloakConfig}
|
||||||
|
onSigninCallback={onSigninCallback}
|
||||||
|
>
|
||||||
|
<AppContent />
|
||||||
|
</AuthProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <AppContent />;
|
||||||
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ jest.mock("../../Contexts/UserContext");
|
|||||||
jest.mock("@material-ui/core/utils/unstable_useId", () =>
|
jest.mock("@material-ui/core/utils/unstable_useId", () =>
|
||||||
jest.fn().mockReturnValue("mui-test-id")
|
jest.fn().mockReturnValue("mui-test-id")
|
||||||
);
|
);
|
||||||
|
jest.useFakeTimers();
|
||||||
|
jest.setSystemTime(new Date(2024, 6, 18, 6, 30, 45, 100));
|
||||||
|
|
||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import auth from "../../services/auth";
|
|||||||
import getTimerWorker from "../../services/getTimerWorker";
|
import getTimerWorker from "../../services/getTimerWorker";
|
||||||
import { parsePayload } from "../../utils/jwt";
|
import { parsePayload } from "../../utils/jwt";
|
||||||
import {getGroups, getProviders} from "../../utils/roles";
|
import {getGroups, getProviders} from "../../utils/roles";
|
||||||
|
import { isKeycloakEnabled } from "../../services/keycloak";
|
||||||
|
|
||||||
const UserContext = React.createContext();
|
const UserContext = React.createContext();
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ export const UserProvider = ({ children }) => {
|
|||||||
const [groups, setGroups] = useState(null);
|
const [groups, setGroups] = useState(null);
|
||||||
const [providers, setProviders] = useState(null);
|
const [providers, setProviders] = useState(null);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
const [authSource, setAuthSource] = useState(null); // 'cognito' or 'keycloak'
|
||||||
let timer;
|
let timer;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -19,8 +21,18 @@ export const UserProvider = ({ children }) => {
|
|||||||
if (!localStorage) return;
|
if (!localStorage) return;
|
||||||
const t = JSON.parse(localStorage.getItem("token"));
|
const t = JSON.parse(localStorage.getItem("token"));
|
||||||
if (!t) return;
|
if (!t) return;
|
||||||
|
|
||||||
|
// Check if it's a Keycloak token (different structure)
|
||||||
|
if (t.authSource === 'keycloak') {
|
||||||
|
setToken(t);
|
||||||
|
setAuthSource('keycloak');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cognito token format
|
||||||
if (!t.idToken || !t.idToken.jwtToken) throw new Error("Invalid token");
|
if (!t.idToken || !t.idToken.jwtToken) throw new Error("Invalid token");
|
||||||
setToken(t);
|
setToken(t);
|
||||||
|
setAuthSource('cognito');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
document.location = signOut();
|
document.location = signOut();
|
||||||
}
|
}
|
||||||
@@ -29,12 +41,16 @@ export const UserProvider = ({ children }) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
|
if (authSource === 'keycloak') {
|
||||||
|
verifyKeycloakToken();
|
||||||
|
} else {
|
||||||
verifyToken();
|
verifyToken();
|
||||||
|
}
|
||||||
return () => {
|
return () => {
|
||||||
if (timer) timer.terminate();
|
if (timer) timer.terminate();
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [token]);
|
}, [token, authSource]);
|
||||||
|
|
||||||
const refreshTokens = async () => {
|
const refreshTokens = async () => {
|
||||||
if (!token || !token.refreshToken || !token.refreshToken.token) return null;
|
if (!token || !token.refreshToken || !token.refreshToken.token) return null;
|
||||||
@@ -42,25 +58,57 @@ export const UserProvider = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const startSessionTimer = () => {
|
const startSessionTimer = () => {
|
||||||
|
let jwtToken;
|
||||||
|
if (authSource === 'keycloak') {
|
||||||
|
jwtToken = token.idToken;
|
||||||
|
} else {
|
||||||
if (!token || !token.idToken || !token.idToken.jwtToken) {
|
if (!token || !token.idToken || !token.idToken.jwtToken) {
|
||||||
throw new Error("No id token");
|
throw new Error("No id token");
|
||||||
}
|
}
|
||||||
const payload = parsePayload(token.idToken.jwtToken);
|
jwtToken = token.idToken.jwtToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = parsePayload(jwtToken);
|
||||||
if (!payload || !payload.exp) throw new Error("Bad id token payload");
|
if (!payload || !payload.exp) throw new Error("Bad id token payload");
|
||||||
const duration = 1000 * payload.exp - new Date().getTime();
|
const duration = 1000 * payload.exp - new Date().getTime();
|
||||||
if (!timer) {
|
if (!timer) {
|
||||||
timer = getTimerWorker();
|
timer = getTimerWorker();
|
||||||
timer.onMessage(async (e) => {
|
timer.onMessage(async (e) => {
|
||||||
if (e.data === "timeout") {
|
if (e.data === "timeout") {
|
||||||
|
if (authSource === 'keycloak') {
|
||||||
|
// For Keycloak, just sign out - OIDC library handles refresh
|
||||||
|
document.location = signOut();
|
||||||
|
} else {
|
||||||
const t = await refreshTokens();
|
const t = await refreshTokens();
|
||||||
if (t && !t.error) return;
|
if (t && !t.error) return;
|
||||||
document.location = signOut();
|
document.location = signOut();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
timer.start(duration);
|
timer.start(duration);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const verifyKeycloakToken = async () => {
|
||||||
|
try {
|
||||||
|
const idToken = token.idToken;
|
||||||
|
const payload = parsePayload(idToken);
|
||||||
|
|
||||||
|
// For Keycloak, we trust the token if it's not expired
|
||||||
|
if (!payload || !payload.exp) throw new Error("Invalid Keycloak token");
|
||||||
|
if (payload.exp * 1000 < Date.now()) throw new Error("Token expired");
|
||||||
|
|
||||||
|
// Extract groups from Keycloak token (realm_access.roles or groups claim)
|
||||||
|
const keycloakGroups = payload.groups || payload.realm_access?.roles || [];
|
||||||
|
setGroups(keycloakGroups);
|
||||||
|
setProviders([]);
|
||||||
|
startSessionTimer();
|
||||||
|
} catch (e) {
|
||||||
|
setError(`Keycloak verify error. ${e.message}`);
|
||||||
|
document.location = signOut();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const verifyToken = async () => {
|
const verifyToken = async () => {
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
@@ -96,6 +144,7 @@ export const UserProvider = ({ children }) => {
|
|||||||
throw new Error(result.message);
|
throw new Error(result.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAuthSource('cognito');
|
||||||
signedIn(result);
|
signedIn(result);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(`Sign in error. ${err.message}`);
|
setError(`Sign in error. ${err.message}`);
|
||||||
@@ -106,13 +155,42 @@ export const UserProvider = ({ children }) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sign in with Keycloak OIDC token
|
||||||
|
const signInWithKeycloak = (oidcUser) => {
|
||||||
|
if (!oidcUser || !oidcUser.id_token) return;
|
||||||
|
|
||||||
|
const keycloakToken = {
|
||||||
|
authSource: 'keycloak',
|
||||||
|
idToken: oidcUser.id_token,
|
||||||
|
accessToken: oidcUser.access_token,
|
||||||
|
refreshToken: oidcUser.refresh_token,
|
||||||
|
profile: oidcUser.profile,
|
||||||
|
};
|
||||||
|
|
||||||
|
setAuthSource('keycloak');
|
||||||
|
setToken(keycloakToken);
|
||||||
|
if (localStorage) {
|
||||||
|
localStorage.setItem("token", JSON.stringify(keycloakToken));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const signOut = () => {
|
const signOut = () => {
|
||||||
setGroups(null);
|
setGroups(null);
|
||||||
setProviders(null);
|
setProviders(null);
|
||||||
setToken(null);
|
setToken(null);
|
||||||
|
setAuthSource(null);
|
||||||
if (localStorage) {
|
if (localStorage) {
|
||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For Keycloak, we need to redirect to Keycloak logout
|
||||||
|
if (authSource === 'keycloak' && isKeycloakEnabled()) {
|
||||||
|
const keycloakUrl = process.env.REACT_APP_KEYCLOAK_URL || 'https://keycloak.mini.cloud.fiskerinc.com';
|
||||||
|
const realm = process.env.REACT_APP_KEYCLOAK_REALM || 'compute-auth';
|
||||||
|
const redirectUri = process.env.REACT_APP_AUTH_CALLBACK_URL;
|
||||||
|
return `${keycloakUrl}/realms/${realm}/protocol/openid-connect/logout?post_logout_redirect_uri=${encodeURIComponent(redirectUri)}`;
|
||||||
|
}
|
||||||
|
|
||||||
return getLogoutURL();
|
return getLogoutURL();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -158,10 +236,12 @@ export const UserProvider = ({ children }) => {
|
|||||||
groups,
|
groups,
|
||||||
providers,
|
providers,
|
||||||
token,
|
token,
|
||||||
|
authSource,
|
||||||
getAuthorizeURL,
|
getAuthorizeURL,
|
||||||
getLogoutURL,
|
getLogoutURL,
|
||||||
setError,
|
setError,
|
||||||
signIn,
|
signIn,
|
||||||
|
signInWithKeycloak,
|
||||||
signOut,
|
signOut,
|
||||||
refresh,
|
refresh,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const transformLogs = (logs) =>
|
|||||||
})
|
})
|
||||||
.flat()
|
.flat()
|
||||||
|
|
||||||
const fromatDateForRequest = (date) => {
|
const formatDateForRequest = (date) => {
|
||||||
const getYear = date.toLocaleString("default", { year: "numeric" });
|
const getYear = date.toLocaleString("default", { year: "numeric" });
|
||||||
const getMonth = date.toLocaleString("default", { month: "2-digit" });
|
const getMonth = date.toLocaleString("default", { month: "2-digit" });
|
||||||
const getDay = date.toLocaleString("default", { day: "2-digit" });
|
const getDay = date.toLocaleString("default", { day: "2-digit" });
|
||||||
@@ -120,7 +120,7 @@ const TRexLogsTable = ({ vin, token, classes }) => {
|
|||||||
const [allLogsFetched, setAllLogsFetched] = useState(false)
|
const [allLogsFetched, setAllLogsFetched] = useState(false)
|
||||||
const [blobSize, setBlobSize] = useState(0)
|
const [blobSize, setBlobSize] = useState(0)
|
||||||
const [currentOffset, setCurrentOffset] = useState(0)
|
const [currentOffset, setCurrentOffset] = useState(0)
|
||||||
const [currectLogLevels, setCurrentLogLevels] = useState({
|
const [currentLogLevels, setCurrentLogLevels] = useState({
|
||||||
trace: true,
|
trace: true,
|
||||||
debug: true,
|
debug: true,
|
||||||
info: true,
|
info: true,
|
||||||
@@ -139,7 +139,7 @@ const TRexLogsTable = ({ vin, token, classes }) => {
|
|||||||
|
|
||||||
let controller = new AbortController()
|
let controller = new AbortController()
|
||||||
const readBlob = async (offset, count) => {
|
const readBlob = async (offset, count) => {
|
||||||
return await api.getTRexLogs(vin, search, fromatDateForRequest(selectedDate), offset, count, "UP", token, controller)
|
return await api.getTRexLogs(vin, search, formatDateForRequest(selectedDate), offset, count, "UP", token, controller)
|
||||||
}
|
}
|
||||||
const getDesiredSize = () => {
|
const getDesiredSize = () => {
|
||||||
return pageSize * pageIndex + pageSize
|
return pageSize * pageIndex + pageSize
|
||||||
@@ -148,7 +148,7 @@ const TRexLogsTable = ({ vin, token, classes }) => {
|
|||||||
return (currentOffset * 100 / blobSize);
|
return (currentOffset * 100 / blobSize);
|
||||||
}
|
}
|
||||||
const getFilteredLogs = (logs) => {
|
const getFilteredLogs = (logs) => {
|
||||||
return logs.filter(log => currectLogLevels[log.level] === true)
|
return logs.filter(log => currentLogLevels[log.level] === true)
|
||||||
}
|
}
|
||||||
const fetchAllLogs = async () => {
|
const fetchAllLogs = async () => {
|
||||||
let fetched = []
|
let fetched = []
|
||||||
@@ -167,7 +167,7 @@ const TRexLogsTable = ({ vin, token, classes }) => {
|
|||||||
fetched = transformLogs(result.data).concat(fetched)
|
fetched = transformLogs(result.data).concat(fetched)
|
||||||
setLogs(fetched)
|
setLogs(fetched)
|
||||||
if (offset >= result.blobSize) {
|
if (offset >= result.blobSize) {
|
||||||
setMessage(`All log for ${fromatDateForRequest(selectedDate)} fetched`)
|
setMessage(`All T.Rex logs for ${formatDateForRequest(selectedDate)} fetched`)
|
||||||
setAllLogsFetched(true)
|
setAllLogsFetched(true)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -175,12 +175,15 @@ const TRexLogsTable = ({ vin, token, classes }) => {
|
|||||||
|
|
||||||
if (fetched.length === 0) {
|
if (fetched.length === 0) {
|
||||||
if (logs.length !== 0) {
|
if (logs.length !== 0) {
|
||||||
setMessage(`No more T.Rex logs for ${fromatDateForRequest(selectedDate)}`)
|
setMessage(`No more T.Rex logs for ${formatDateForRequest(selectedDate)}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setTotal(0)
|
setTotal(0)
|
||||||
const msg = `Cannot fetch logs for ${fromatDateForRequest(selectedDate)}`
|
if (fetched.error) {
|
||||||
setMessage(msg)
|
setMessage(`Error fetching T.Rex logs for ${formatDateForRequest(selectedDate)}`)
|
||||||
|
} else {
|
||||||
|
setMessage(`No T.Rex logs for ${formatDateForRequest(selectedDate)}`)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setCurrentOffset(offset)
|
setCurrentOffset(offset)
|
||||||
@@ -221,7 +224,7 @@ const TRexLogsTable = ({ vin, token, classes }) => {
|
|||||||
})();
|
})();
|
||||||
return () => controller?.abort()
|
return () => controller?.abort()
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [vin, token, search, pageIndex, pageSize, selectedDate, currectLogLevels]);
|
}, [vin, token, search, pageIndex, pageSize, selectedDate, currentLogLevels]);
|
||||||
|
|
||||||
const handleChangePageSize = (event) => {
|
const handleChangePageSize = (event) => {
|
||||||
setPageSize(parseInt(event.target.value, 10));
|
setPageSize(parseInt(event.target.value, 10));
|
||||||
@@ -241,13 +244,20 @@ const TRexLogsTable = ({ vin, token, classes }) => {
|
|||||||
const handleNewFilter = (event) => {
|
const handleNewFilter = (event) => {
|
||||||
setPageIndex(0)
|
setPageIndex(0)
|
||||||
setCurrentLogLevels({
|
setCurrentLogLevels({
|
||||||
...currectLogLevels,
|
...currentLogLevels,
|
||||||
[event.target.defaultValue]: event.target.checked,
|
[event.target.defaultValue]: event.target.checked,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearch = (query) => {
|
const handleSearch = (query) => {
|
||||||
|
if (query !== search) {
|
||||||
|
setPageIndex(0);
|
||||||
|
setCurrentOffset(0)
|
||||||
|
setLogs([])
|
||||||
|
setAllLogsFetched(false)
|
||||||
|
setBlobSize(0)
|
||||||
setSearch(query);
|
setSearch(query);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -299,7 +309,7 @@ const TRexLogsTable = ({ vin, token, classes }) => {
|
|||||||
|
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
{
|
{
|
||||||
blobSize === 0 ? `No logs for ${fromatDateForRequest(selectedDate)}` :
|
blobSize === 0 ? `No logs for ${formatDateForRequest(selectedDate)}` :
|
||||||
`Read ${getReadPercentage().toFixed(2)}% of logs`
|
`Read ${getReadPercentage().toFixed(2)}% of logs`
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Button } from "@material-ui/core";
|
import { Button } from "@material-ui/core";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
|
import { useAuth } from "react-oidc-context";
|
||||||
|
|
||||||
import { useUserContext } from "../Contexts/UserContext";
|
import { useUserContext } from "../Contexts/UserContext";
|
||||||
import useStyles from "../useStyles";
|
import useStyles from "../useStyles";
|
||||||
|
import { isKeycloakEnabled } from "../../services/keycloak";
|
||||||
|
|
||||||
const getCode = (search) => {
|
const getCode = (search) => {
|
||||||
if (!search) return null;
|
if (!search) return null;
|
||||||
@@ -11,7 +13,66 @@ const getCode = (search) => {
|
|||||||
return s.get("code");
|
return s.get("code");
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function SignInForm() {
|
// Keycloak-enabled version of the form - only rendered when inside AuthProvider
|
||||||
|
function KeycloakSignInForm() {
|
||||||
|
const classes = useStyles();
|
||||||
|
const { getAuthorizeURL, signIn, signInWithKeycloak, fetching } = useUserContext();
|
||||||
|
const auth = useAuth();
|
||||||
|
|
||||||
|
// Handle Cognito callback
|
||||||
|
useEffect(() => {
|
||||||
|
const code = getCode(document.location.search);
|
||||||
|
// Only process if it's a Cognito code (not a Keycloak callback)
|
||||||
|
if (!code || auth?.isLoading || auth?.isAuthenticated) return;
|
||||||
|
signIn(code);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Handle Keycloak authentication callback
|
||||||
|
useEffect(() => {
|
||||||
|
if (auth?.isAuthenticated && auth?.user) {
|
||||||
|
signInWithKeycloak(auth.user);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [auth?.isAuthenticated, auth?.user]);
|
||||||
|
|
||||||
|
const handleKeycloakLogin = () => {
|
||||||
|
auth.signinRedirect();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx(classes.paper, classes.textJustifyAlign)}>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
className={classes.submit}
|
||||||
|
href={getAuthorizeURL()}
|
||||||
|
disabled={fetching}
|
||||||
|
>
|
||||||
|
{fetching ? "Please wait..." : "Sign In with Cognito"}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div style={{ margin: '16px 0', textAlign: 'center', color: '#666' }}>
|
||||||
|
— or —
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
className={classes.submit}
|
||||||
|
onClick={handleKeycloakLogin}
|
||||||
|
disabled={fetching || auth?.isLoading}
|
||||||
|
>
|
||||||
|
{auth?.isLoading ? "Loading..." : "Sign In with Keycloak"}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<p><strong>Note: Your email address will be used as the user id</strong></p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard Cognito-only form
|
||||||
|
function CognitoSignInForm() {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const { getAuthorizeURL, signIn, fetching } = useUserContext();
|
const { getAuthorizeURL, signIn, fetching } = useUserContext();
|
||||||
|
|
||||||
@@ -38,3 +99,10 @@ export default function SignInForm() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function SignInForm() {
|
||||||
|
if (isKeycloakEnabled()) {
|
||||||
|
return <KeycloakSignInForm />;
|
||||||
|
}
|
||||||
|
return <CognitoSignInForm />;
|
||||||
|
}
|
||||||
|
|||||||
21
src/services/keycloak.js
Normal file
21
src/services/keycloak.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Keycloak OIDC configuration
|
||||||
|
const KEYCLOAK_URL = process.env.REACT_APP_KEYCLOAK_URL || 'https://keycloak.mini.cloud.fiskerinc.com';
|
||||||
|
const KEYCLOAK_REALM = process.env.REACT_APP_KEYCLOAK_REALM || 'compute-auth';
|
||||||
|
const KEYCLOAK_CLIENT_ID = process.env.REACT_APP_KEYCLOAK_CLIENT_ID || 'ota-portal';
|
||||||
|
const CALLBACK_URL = process.env.REACT_APP_AUTH_CALLBACK_URL;
|
||||||
|
|
||||||
|
export const keycloakConfig = {
|
||||||
|
authority: `${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}`,
|
||||||
|
client_id: KEYCLOAK_CLIENT_ID,
|
||||||
|
redirect_uri: CALLBACK_URL,
|
||||||
|
post_logout_redirect_uri: CALLBACK_URL,
|
||||||
|
response_type: 'code',
|
||||||
|
scope: 'openid',
|
||||||
|
// Handle silent renew and callback automatically
|
||||||
|
automaticSilentRenew: true,
|
||||||
|
loadUserInfo: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isKeycloakEnabled = () => {
|
||||||
|
return process.env.REACT_APP_KEYCLOAK_ENABLED === 'true';
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user