mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 08:42:13 +02:00
Compare commits
2 Commits
files-note
...
monitor-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71a0edb7dd | ||
|
|
a53b24ed05 |
17
.github/instructions/instructions.md
vendored
17
.github/instructions/instructions.md
vendored
@@ -1,17 +0,0 @@
|
||||
---
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
# Building and Compiling
|
||||
|
||||
If you would like to compile or build any project. Please cd into the directory and run the following command:
|
||||
|
||||
```
|
||||
npm run compile
|
||||
```
|
||||
|
||||
Ths will make sure there are no type / syntax errors.
|
||||
|
||||
# Typescript Types.
|
||||
|
||||
Please do not use "any" types. Please create proper types where required.
|
||||
370
.github/workflows/build.yml
vendored
370
.github/workflows/build.yml
vendored
@@ -16,22 +16,14 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Accounts/Dockerfile .
|
||||
run: sudo docker build -f ./Accounts/Dockerfile .
|
||||
|
||||
docker-build-isolated-vm:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -39,137 +31,14 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./IsolatedVM/Dockerfile .
|
||||
|
||||
docker-build-home:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Home/Dockerfile .
|
||||
|
||||
docker-build-worker:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Worker/Dockerfile .
|
||||
|
||||
docker-build-workflow:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Workflow/Dockerfile .
|
||||
|
||||
docker-build-api-reference:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./APIReference/Dockerfile .
|
||||
|
||||
docker-build-docs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Docs/Dockerfile .
|
||||
run: sudo docker build -f ./IsolatedVM/Dockerfile .
|
||||
|
||||
|
||||
docker-build-otel-collector:
|
||||
@@ -178,22 +47,14 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./OTelCollector/Dockerfile .
|
||||
run: sudo docker build -f ./OTelCollector/Dockerfile .
|
||||
|
||||
docker-build-app:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -201,23 +62,15 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./App/Dockerfile .
|
||||
run: sudo docker build -f ./App/Dockerfile .
|
||||
|
||||
|
||||
docker-build-copilot:
|
||||
@@ -226,22 +79,14 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Copilot/Dockerfile .
|
||||
run: sudo docker build -f ./Copilot/Dockerfile .
|
||||
|
||||
docker-build-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -249,23 +94,15 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
|
||||
# build image for accounts service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./E2E/Dockerfile .
|
||||
run: sudo docker build -f ./E2E/Dockerfile .
|
||||
|
||||
docker-build-admin-dashboard:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -273,22 +110,14 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image for home
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./AdminDashboard/Dockerfile .
|
||||
run: sudo docker build -f ./AdminDashboard/Dockerfile .
|
||||
|
||||
docker-build-dashboard:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -296,22 +125,30 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image for home
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Dashboard/Dockerfile .
|
||||
run: sudo docker build -f ./Dashboard/Dockerfile .
|
||||
|
||||
docker-build-haraka:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
run: npm run prerun
|
||||
|
||||
# build images
|
||||
- name: build docker image
|
||||
run: sudo docker build -f ./Haraka/Dockerfile .
|
||||
|
||||
|
||||
docker-build-probe:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -319,114 +156,29 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image probe api
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Probe/Dockerfile .
|
||||
run: sudo docker build -f ./Probe/Dockerfile .
|
||||
|
||||
docker-build-probe-ingest:
|
||||
docker-build-ingestor:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image probe api
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./ProbeIngest/Dockerfile .
|
||||
|
||||
docker-build-server-monitor-ingest:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
|
||||
# build image probe api
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./ServerMonitorIngest/Dockerfile .
|
||||
|
||||
docker-build-telemetry:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
|
||||
# build image probe api
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Telemetry/Dockerfile .
|
||||
|
||||
docker-build-incoming-request-ingest:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
|
||||
# build image probe api
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./IncomingRequestIngest/Dockerfile .
|
||||
run: sudo docker build -f ./Ingestor/Dockerfile .
|
||||
|
||||
docker-build-status-page:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -434,22 +186,14 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image for home
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./StatusPage/Dockerfile .
|
||||
run: sudo docker build -f ./StatusPage/Dockerfile .
|
||||
|
||||
docker-build-test-server:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -457,19 +201,11 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Preinstall
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
command: npm run prerun
|
||||
run: npm run prerun
|
||||
|
||||
# build image for mail service
|
||||
- name: build docker image
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./TestServer/Dockerfile .
|
||||
run: sudo docker build -f ./TestServer/Dockerfile .
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -31,6 +31,8 @@ jobs:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript', 'typescript', 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
|
||||
4
.github/workflows/common-jobs.yaml
vendored
4
.github/workflows/common-jobs.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Helm
|
||||
run: |
|
||||
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
- run: sudo apt-get update
|
||||
- run: sudo apt-get install -y curl gcc
|
||||
- run: sudo apt-get install -y build-essential
|
||||
|
||||
403
.github/workflows/compile.yml
vendored
403
.github/workflows/compile.yml
vendored
@@ -15,187 +15,110 @@ jobs:
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Accounts
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Accounts && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
- run: cd Accounts && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-isolated-vm:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- name: Compile IsolatedVM
|
||||
uses: nick-fields/retry@v3
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd IsolatedVM && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-common-server:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd IsolatedVM && npm install && npm run compile && npm run dep-check
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-common-ui:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonUI && npm install --force && npm run compile && npm run dep-check
|
||||
|
||||
compile-common:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
- name: Compile Common
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Common && npm install && npm run compile && npm run dep-check
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-app:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- name: Compile App
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd App && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-home:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Home
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Home && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-worker:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Worker
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Worker && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-workflow:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Workflow
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Workflow && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-api-reference:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- name: Compile API Reference
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd APIReference && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-docs-reference:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Docs Reference
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Docs && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
- run: cd App && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-copilot:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Copilot
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Copilot && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd Copilot && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-nginx:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
|
||||
- name: Compile Nginx
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Nginx && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
- run: cd Nginx && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-infrastructure-agent:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -205,12 +128,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
# Setup Go
|
||||
- uses: actions/setup-go@v5
|
||||
- name: Compile Infrastructure Agent
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd InfrastructureAgent && go build .
|
||||
- run: cd InfrastructureAgent && go build .
|
||||
|
||||
|
||||
compile-admin-dashboard:
|
||||
@@ -218,190 +136,111 @@ jobs:
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
|
||||
- name: Compile Admin Dashboard
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd AdminDashboard && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
- run: cd AdminDashboard && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-dashboard:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
|
||||
- name: Compile Dashboard
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Dashboard && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
- run: cd Dashboard && npm install && npm run compile && npm run dep-check
|
||||
|
||||
|
||||
compile-model:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd Model && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
- run: sudo apt-get update
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- name: Compile E2E
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd E2E && npm install && npm run compile && npm run dep-check
|
||||
- run: cd E2E && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-probe:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Probe
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Probe && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd Probe && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-probe-ingest:
|
||||
compile-ingestor:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Probe Ingest
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd ProbeIngest && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-server-monitor-ingest:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Server Monitor Ingest
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd ServerMonitorIngest && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-telemetry:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Telemetry
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Telemetry && npm install && npm run compile && npm run dep-check
|
||||
|
||||
|
||||
compile-incoming-request-ingest:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Incoming Request Ingest
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd IncomingRequestIngest && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd Ingestor && npm install && npm run compile && npm run dep-check
|
||||
|
||||
|
||||
compile-status-page:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
|
||||
- name: Compile Status Page
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd StatusPage && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonUI && npm install --force
|
||||
- run: cd StatusPage && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-test-server:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- name: Compile Test Server
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd TestServer && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-mcp:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- name: Compile MCP
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd MCP && npm update @oneuptime/common && npm install && npm run compile && npm run dep-check
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd TestServer && npm install && npm run compile && npm run dep-check
|
||||
49
.github/workflows/npm-audit-fix.yml
vendored
49
.github/workflows/npm-audit-fix.yml
vendored
@@ -1,49 +0,0 @@
|
||||
name: NPM Audit Fix
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
npm-audit-fix:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Run npm audit fix across packages
|
||||
run: npm run audit-fix
|
||||
|
||||
- name: Detect changes
|
||||
id: changes
|
||||
run: |
|
||||
if git status --porcelain | grep .; then
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Create pull request
|
||||
if: steps.changes.outputs.has_changes == 'true'
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
commit-message: "chore: npm audit fix"
|
||||
title: "chore: npm audit fix"
|
||||
body: |
|
||||
Automated npm audit fix run.
|
||||
Workflow: ${{ github.workflow }}
|
||||
Run ID: ${{ github.run_id }}
|
||||
branch: chore/npm-audit-fix
|
||||
delete-branch: true
|
||||
74
.github/workflows/openapi-spec-generation.yml
vendored
74
.github/workflows/openapi-spec-generation.yml
vendored
@@ -1,74 +0,0 @@
|
||||
name: OpenAPI Spec Generation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'hotfix-*'
|
||||
- 'release'
|
||||
|
||||
jobs:
|
||||
generate-openapi-spec:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{ github.run_number }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Common dependencies
|
||||
run: cd Common && npm install
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Install Script dependencies
|
||||
run: cd Scripts && npm install
|
||||
|
||||
- name: Generate OpenAPI specification
|
||||
run: npm run generate-openapi-spec
|
||||
|
||||
- name: Check if OpenAPI spec was generated
|
||||
run: |
|
||||
if [ -f "./openapi.json" ]; then
|
||||
echo "✅ OpenAPI spec file generated successfully"
|
||||
echo "📄 File size: $(du -h ./openapi.json | cut -f1)"
|
||||
echo "📊 Spec contains $(jq '.paths | length' ./openapi.json) API paths"
|
||||
echo "🏷️ API version: $(jq -r '.info.version' ./openapi.json)"
|
||||
echo "📝 API title: $(jq -r '.info.title' ./openapi.json)"
|
||||
else
|
||||
echo "❌ OpenAPI spec file was not generated"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Validate OpenAPI spec format
|
||||
run: |
|
||||
# Check if the file is valid JSON
|
||||
if jq empty ./openapi.json; then
|
||||
echo "✅ OpenAPI spec is valid JSON"
|
||||
else
|
||||
echo "❌ OpenAPI spec is not valid JSON"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if it has required OpenAPI fields
|
||||
if jq -e '.openapi and .info and .paths' ./openapi.json > /dev/null; then
|
||||
echo "✅ OpenAPI spec has required fields"
|
||||
else
|
||||
echo "❌ OpenAPI spec missing required fields (openapi, info, paths)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Upload OpenAPI spec as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openapi-spec
|
||||
path: ./openapi.json
|
||||
retention-days: 30
|
||||
2419
.github/workflows/release.yml
vendored
2419
.github/workflows/release.yml
vendored
File diff suppressed because it is too large
Load Diff
2
.github/workflows/reliability-copilot.yml
vendored
2
.github/workflows/reliability-copilot.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
# Run Reliability Copilot in Docker Container
|
||||
# Run Reliability Copilot in Doker Container
|
||||
- name: Run Copilot
|
||||
run: |
|
||||
docker run --rm \
|
||||
|
||||
101
.github/workflows/terraform-provider-generation.yml
vendored
101
.github/workflows/terraform-provider-generation.yml
vendored
@@ -1,101 +0,0 @@
|
||||
name: Terraform Provider Generation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
- develop
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
jobs:
|
||||
generate-terraform-provider:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{ github.run_number }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
cache: 'npm'
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
|
||||
- name: Install Common dependencies
|
||||
run: cd Common && npm install
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Install Script dependencies
|
||||
run: cd Scripts && npm install
|
||||
|
||||
- name: Generate Terraform provider
|
||||
run: npm run generate-terraform-provider
|
||||
|
||||
- name: Verify provider generation
|
||||
run: |
|
||||
PROVIDER_DIR="./Terraform"
|
||||
|
||||
# Check if provider directory was created
|
||||
if [ ! -d "$PROVIDER_DIR" ]; then
|
||||
echo "❌ Terraform provider directory not created"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Provider directory created: $PROVIDER_DIR"
|
||||
|
||||
# Count generated files
|
||||
GO_FILES=$(find "$PROVIDER_DIR" -name "*.go" | wc -l)
|
||||
echo "📊 Generated Go files: $GO_FILES"
|
||||
|
||||
if [ "$GO_FILES" -eq 0 ]; then
|
||||
echo "❌ No Go files were generated"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for essential files
|
||||
if [ -f "$PROVIDER_DIR/go.mod" ]; then
|
||||
echo "✅ Go module file created"
|
||||
fi
|
||||
|
||||
if [ -f "$PROVIDER_DIR/README.md" ]; then
|
||||
echo "✅ Documentation created"
|
||||
fi
|
||||
|
||||
# Show directory structure for debugging
|
||||
echo "📁 Provider directory structure:"
|
||||
ls -la "$PROVIDER_DIR" || true
|
||||
|
||||
- name: Test Go build
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
PROVIDER_DIR="./Terraform"
|
||||
if [ -d "$PROVIDER_DIR" ] && [ -f "$PROVIDER_DIR/go.mod" ]; then
|
||||
cd "$PROVIDER_DIR"
|
||||
echo "🔨 Testing Go build..."
|
||||
go mod tidy
|
||||
go build -v ./...
|
||||
echo "✅ Go build successful"
|
||||
else
|
||||
echo "⚠️ Cannot test build - missing go.mod or provider directory"
|
||||
fi
|
||||
|
||||
- name: Upload Terraform provider as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Terraform
|
||||
path: ./Terraform/
|
||||
retention-days: 30
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Incoming Request Ingest Test
|
||||
name: App Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -13,9 +13,8 @@ jobs:
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd IncomingRequestIngest && npm install && npm run test
|
||||
|
||||
node-version: 18.3.0
|
||||
- run: cd App && npm install && npm run test
|
||||
2163
.github/workflows/test-release.yaml
vendored
2163
.github/workflows/test-release.yaml
vendored
File diff suppressed because it is too large
Load Diff
24
.github/workflows/test.common-server.yaml
vendored
Normal file
24
.github/workflows/test.common-server.yaml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Common Server Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'hotfix-*' # excludes hotfix branches
|
||||
- 'release'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd CommonServer && bash test-setup.sh
|
||||
- run: export $(grep -v '^#' config.env | xargs) && cd CommonServer && rm -rf build && npm run test
|
||||
11
.github/workflows/test.common.yaml
vendored
11
.github/workflows/test.common.yaml
vendored
@@ -12,12 +12,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
BILLING_PRIVATE_KEY: ${{secrets.TEST_BILLING_PRIVATE_KEY}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && bash test-setup.sh
|
||||
- run: cd Common && npm install && rm -rf build && npm run test
|
||||
node-version: 18.3.0
|
||||
- run: cd Model && npm install
|
||||
- run: cd Common && npm install && npm run test
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: MCP Server Test
|
||||
name: CommonUI Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -13,9 +13,11 @@ jobs:
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd MCP && npm install && npm run test
|
||||
- run: cd Model && npm install
|
||||
- run: cd CommonUI && npm install --force && npm run test
|
||||
|
||||
58
.github/workflows/test.e2e.yaml
vendored
Normal file
58
.github/workflows/test.e2e.yaml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: E2E Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'hotfix-*' # excludes hotfix branches
|
||||
- 'release'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
# Docker compose needs a lot of space to build images, so we need to free up some space first in the GitHub Actions runner
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.3.0
|
||||
- run: npm run prerun && bash ./Tests/Scripts/enable-billing-env-var.sh
|
||||
- run: npm run dev
|
||||
- name: Wait for server to start
|
||||
run: bash ./Tests/Scripts/status-check.sh http://localhost
|
||||
- name: Run E2E Tests. Run docker container e2e in docker compose file
|
||||
run: export $(grep -v '^#' config.env | xargs) && docker compose -f docker-compose.dev.yml up --exit-code-from e2e --abort-on-container-exit e2e || (docker compose -f docker-compose.dev.yml logs e2e && exit 1)
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
# Run this on failure
|
||||
if: failure()
|
||||
with:
|
||||
# Name of the artifact to upload.
|
||||
# Optional. Default is 'artifact'
|
||||
name: test-results
|
||||
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
# Required.
|
||||
path: |
|
||||
./E2E
|
||||
|
||||
|
||||
# Duration after which artifact will expire in days. 0 means using default retention.
|
||||
# Minimum 1 day.
|
||||
# Maximum 90 days unless changed from the repository settings page.
|
||||
# Optional. Defaults to repository settings.
|
||||
retention-days: 7
|
||||
@@ -1,4 +1,4 @@
|
||||
name: ProbeIngest Test
|
||||
name: Ingestor Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd ProbeIngest && npm install && npm run test
|
||||
node-version: 18.3.0
|
||||
- run: cd Ingestor && npm install && npm run test
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Telemetry Test
|
||||
name: Model Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -13,10 +13,11 @@ jobs:
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd Telemetry && npm install && npm run test
|
||||
|
||||
- run: cd Model && npm install
|
||||
- run: cd Model && npm install && npm run test
|
||||
|
||||
7
.github/workflows/test.probe.yaml
vendored
7
.github/workflows/test.probe.yaml
vendored
@@ -13,11 +13,12 @@ jobs:
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 18.3.0
|
||||
- run: cd Common && npm install
|
||||
- run: cd CommonServer && npm install
|
||||
- run: cd Probe && npm install
|
||||
- run: cd Probe && npm run test
|
||||
|
||||
42
.github/workflows/test.yaml
vendored
42
.github/workflows/test.yaml
vendored
@@ -1,42 +0,0 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'hotfix-*' # excludes hotfix branches
|
||||
- 'release'
|
||||
|
||||
jobs:
|
||||
test-app:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd App && npm install && npm run test
|
||||
|
||||
test-home:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Home && npm install && npm run test
|
||||
|
||||
test-worker:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Worker && npm install && npm run test
|
||||
22
.gitignore
vendored
22
.gitignore
vendored
@@ -86,6 +86,9 @@ Backups/*.tar
|
||||
|
||||
.env
|
||||
|
||||
Haraka/dkim/keys/private_base64.txt
|
||||
Haraka/dkim/keys/public_base64.txt
|
||||
|
||||
.eslintcache
|
||||
|
||||
HelmChart/Values/*.values.yaml
|
||||
@@ -109,22 +112,3 @@ App/greenlock/greenlock.d/config.json
|
||||
App/greenlock/greenlock.d/config.json.bak
|
||||
Examples/otel-dotnet/bin/Debug/net6.0/Grpc.Core.Api.dll.txt
|
||||
InfrastructureAgent/oneuptime-infrastructure-agent
|
||||
|
||||
# ESLint cache
|
||||
.eslintcache*
|
||||
|
||||
# Terraform generated files
|
||||
openapi.json
|
||||
|
||||
Terraform/**
|
||||
|
||||
TerraformTest/**
|
||||
|
||||
terraform-provider-example/**
|
||||
|
||||
# MCP Server
|
||||
MCP/build/
|
||||
MCP/.env
|
||||
MCP/node_modules
|
||||
Dashboard/public/sw.js
|
||||
.claude/settings.local.json
|
||||
|
||||
@@ -1 +1 @@
|
||||
# Description: Copilot will run this script before we commit the changes to your repository.
|
||||
# Description: Copilot will run this script before we commit the changes to your repository.
|
||||
@@ -51,3 +51,6 @@ licenses/*
|
||||
certifications/*
|
||||
ApiReference/public/assets/*
|
||||
JavaScriptSDK/src/cli/server-monitor/out/scripts/prettify/*
|
||||
|
||||
|
||||
CommonServer/Tests/TestingUtils/__mocks__/Stripe.mock.ts
|
||||
136
.vscode/launch.json
vendored
136
.vscode/launch.json
vendored
@@ -63,76 +63,6 @@
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Home",
|
||||
"name": "Home: Debug with Docker",
|
||||
"port": 9212,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Worker",
|
||||
"name": "Worker: Debug with Docker",
|
||||
"port": 8734,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Workflow",
|
||||
"name": "Workflow: Debug with Docker",
|
||||
"port": 8735,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Docs",
|
||||
"name": "Docs: Debug with Docker",
|
||||
"port": 8738,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/APIReference",
|
||||
"name": "API Reference: Debug with Docker",
|
||||
"port": 8737,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/TestServer",
|
||||
@@ -151,7 +81,7 @@
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Probe",
|
||||
"name": "Probe: Debug with Docker",
|
||||
"port": 9229,
|
||||
"port": 9655,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
@@ -163,8 +93,8 @@
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/ProbeIngest",
|
||||
"name": "ProbeIngest: Debug with Docker",
|
||||
"localRoot": "${workspaceFolder}/Ingestor",
|
||||
"name": "Ingestor: Debug with Docker",
|
||||
"port": 9932,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
@@ -175,48 +105,6 @@
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/ServerMonitorIngest",
|
||||
"name": "ServerMonitorIngest: Debug with Docker",
|
||||
"port": 9941,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/IncomingRequestIngest",
|
||||
"name": "IncomingRequestIngest: Debug with Docker",
|
||||
"port": 9933,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Telemetry",
|
||||
"name": "Telemetry: Debug with Docker",
|
||||
"port": 9938,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/IsolatedVM",
|
||||
@@ -259,6 +147,20 @@
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/Workers",
|
||||
"name": "Workers: Debug with Docker",
|
||||
"port": 9654,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/StatusPage",
|
||||
@@ -316,12 +218,12 @@
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"name": "Common: Debug Tests",
|
||||
"name": "CommonServer: Debug Tests",
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true,
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceRoot}/Common",
|
||||
"cwd": "${workspaceRoot}/CommonServer",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": [
|
||||
"run-script",
|
||||
|
||||
30
APIReference/.gitignore
vendored
30
APIReference/.gitignore
vendored
@@ -1,30 +0,0 @@
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
#/backend/node_modules
|
||||
/kubernetes
|
||||
/node_modules
|
||||
.idea
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
yarn.lock
|
||||
|
||||
**/*/paymentService.test.js
|
||||
apiTest.rest
|
||||
|
||||
application_security_dir
|
||||
container_security_dir
|
||||
|
||||
# coverage
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
/greenlock.d/config.json
|
||||
/greenlock.d/config.json.bak
|
||||
/.greenlockrc
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"query": {
|
||||
"age": {
|
||||
"_type": "EqualTo",
|
||||
value: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"query": {
|
||||
"age": {
|
||||
"_type": "GreaterThanOrNull",
|
||||
"value": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"query": {
|
||||
"labels": {
|
||||
"_type": "Includes",
|
||||
"value": [
|
||||
"aaa00000-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
|
||||
"bbb00000-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"query": {
|
||||
"age": {
|
||||
"_type": "LessThanOrNull",
|
||||
"value": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import APIReferenceRoutes from "./Routes";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import InfrastructureStatus from "Common/Server/Infrastructure/Status";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import App from "Common/Server/Utils/StartServer";
|
||||
import Telemetry from "Common/Server/Utils/Telemetry";
|
||||
import "ejs";
|
||||
|
||||
const APP_NAME: string = "reference";
|
||||
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
// Initialize telemetry
|
||||
Telemetry.init({
|
||||
serviceName: APP_NAME,
|
||||
});
|
||||
|
||||
const statusCheck: PromiseVoidFunction = async (): Promise<void> => {
|
||||
// Check the status of infrastructure components
|
||||
return await InfrastructureStatus.checkStatusWithRetry({
|
||||
checkClickhouseStatus: false,
|
||||
checkPostgresStatus: false,
|
||||
checkRedisStatus: false,
|
||||
retryCount: 3,
|
||||
});
|
||||
};
|
||||
|
||||
// Initialize the app with service name and status checks
|
||||
await App.init({
|
||||
appName: APP_NAME,
|
||||
statusOptions: {
|
||||
liveCheck: statusCheck,
|
||||
readyCheck: statusCheck,
|
||||
},
|
||||
});
|
||||
|
||||
await APIReferenceRoutes.init();
|
||||
|
||||
// Add default routes to the app
|
||||
await App.addDefaultRoutes();
|
||||
} catch (err) {
|
||||
logger.error("App Init Failed:");
|
||||
logger.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
init().catch((err: Error) => {
|
||||
logger.error(err);
|
||||
logger.error("Exiting node process");
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
# README
|
||||
|
||||
This README would normally document whatever steps are necessary to get your application up and running.
|
||||
|
||||
### What is this repository for?
|
||||
|
||||
- Quick summary
|
||||
- Version
|
||||
- [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
||||
|
||||
### How do I get set up?
|
||||
|
||||
- Summary of set up
|
||||
- Configuration
|
||||
- Dependencies
|
||||
- Database configuration
|
||||
- How to run tests
|
||||
- Deployment instructions
|
||||
|
||||
### Contribution guidelines
|
||||
|
||||
- Writing tests
|
||||
- Code review
|
||||
- Other guidelines
|
||||
|
||||
### Who do I talk to?
|
||||
|
||||
- Repo owner or admin
|
||||
- Other community or team contact
|
||||
@@ -1,45 +0,0 @@
|
||||
import {
|
||||
Host,
|
||||
HttpProtocol,
|
||||
IsBillingEnabled,
|
||||
} from "Common/Server/EnvironmentConfig";
|
||||
import { ViewsPath } from "../Utils/Config";
|
||||
import ResourceUtil, { ModelDocumentation } from "../Utils/Resources";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
|
||||
// Fetch a list of resources used in the application
|
||||
const Resources: Array<ModelDocumentation> = ResourceUtil.getResources();
|
||||
|
||||
export default class ServiceHandler {
|
||||
// Handles the HTTP response for a given request
|
||||
public static async executeResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
let pageTitle: string = "";
|
||||
let pageDescription: string = "";
|
||||
|
||||
// Get the 'page' parameter from the request
|
||||
const page: string | undefined = req.params["page"];
|
||||
const pageData: Dictionary<unknown> = {
|
||||
hostUrl: new URL(HttpProtocol, Host).toString(),
|
||||
};
|
||||
|
||||
// Set the default page title and description
|
||||
pageTitle = "OneUptime OpenAPI Specification";
|
||||
pageDescription =
|
||||
"Learn more about the OpenAPI specification for OneUptime";
|
||||
|
||||
// Render the response using the given view and data
|
||||
return res.render(`${ViewsPath}/pages/index`, {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
pageTitle: pageTitle,
|
||||
enableGoogleTagManager: IsBillingEnabled,
|
||||
pageDescription: pageDescription,
|
||||
pageData: pageData,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export const ViewsPath: string = "/usr/src/app/views";
|
||||
export const StaticPath: string = "/usr/src/app/Static";
|
||||
export const CodeExamplesPath: string = "/usr/src/app/CodeExamples";
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"watch": ["./","../Common/Server", "../Common/Types", "../Common/Utils", "../Common/Models"],
|
||||
"ext": "ts,tsx",
|
||||
"ignore": [
|
||||
"./node_modules/**",
|
||||
"./public/**",
|
||||
"./bin/**",
|
||||
"./build/**",
|
||||
"greenlock.d/*"
|
||||
],
|
||||
"watchOptions": {"useFsEvents": false, "interval": 500},
|
||||
"env": {"TS_NODE_TRANSPILE_ONLY": "1", "TS_NODE_FILES": "false"},
|
||||
"exec": "node -r ts-node/register/transpile-only Index.ts"
|
||||
}
|
||||
4262
APIReference/package-lock.json
generated
4262
APIReference/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "@oneuptime/api-reference",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "Index.ts",
|
||||
"scripts": {
|
||||
"start": "export NODE_OPTIONS='--max-old-space-size=8096' && node --require ts-node/register Index.ts",
|
||||
"compile": "tsc",
|
||||
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
|
||||
"dev": "npx nodemon",
|
||||
"audit": "npm audit --audit-level=low",
|
||||
"dep-check": "npm install -g depcheck && depcheck ./ --skip-missing=true",
|
||||
"test": "rm -rf build && jest --detectOpenHandles --passWithNoTests",
|
||||
"coverage": "jest --detectOpenHandles --coverage"
|
||||
},
|
||||
"author": "OneUptime <hello@oneuptime.com> (https://oneuptime.com/)",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"ejs": "^3.1.9",
|
||||
"ts-node": "^10.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/node": "^17.0.31",
|
||||
"jest": "^28.1.0",
|
||||
"nodemon": "^2.0.20"
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
{
|
||||
"ts-node": {
|
||||
// these options are overrides used only by ts-node
|
||||
// same as the --compilerOptions flag and the TS_NODE_COMPILER_OPTIONS environment variable
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"resolveJsonModule": true,
|
||||
}
|
||||
},
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2017" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"jsx": "react" /* Specify what JSX code is generated. */,
|
||||
"experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
|
||||
/* Modules */
|
||||
// "module": "es2022" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
"types": ["node", "jest"], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./build/dist", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
"noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||
"strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
|
||||
"strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
"strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
|
||||
"strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
"noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
|
||||
"useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||
"alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
"noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
|
||||
"noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
|
||||
"exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
"noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
"noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
"noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
"noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
"noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
<main class="py-16">
|
||||
<article class="prose ">
|
||||
<h1>OneUptime OpenAPI Specification</h1>
|
||||
<p class="lead">In this guide, we will look at how to import and work with the OneUptime OpenAPI specification. The OpenAPI spec provides a comprehensive reference for all available API endpoints.</p>
|
||||
<p>The OneUptime API follows the OpenAPI 3.0 specification, which provides a standardized way to describe REST APIs. You can import our OpenAPI spec into various tools like Swagger Editor, Postman, or other API documentation tools to explore and test our endpoints.</p>
|
||||
<h2 id="importing-spec" class="scroll-mt-24">
|
||||
Importing the OpenAPI Spec
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 items-start gap-x-16 gap-y-10">
|
||||
<div class="[&>:first-child]:mt-0 [&>:last-child]:mb-0">
|
||||
<p>To import the OneUptime OpenAPI specification into Swagger Editor, follow these simple steps:</p>
|
||||
<div class="my-6">
|
||||
<ol class="list-decimal pl-6">
|
||||
<li class="mb-2">Open <a href="https://editor.swagger.io" target="_blank" class="text-blue-600 hover:text-blue-800">editor.swagger.io</a> in your web browser</li>
|
||||
<li class="mb-2">Click on <strong>File</strong> in the menu bar</li>
|
||||
<li class="mb-2">Select <strong>Import URL</strong> from the dropdown menu</li>
|
||||
<li class="mb-2">Enter the URL: <code class="inline-code"><%= pageData.hostUrl %>api/openapi/spec</code></li>
|
||||
<li class="mb-2">Click <strong>Import</strong> to load the specification</li>
|
||||
</ol>
|
||||
</div>
|
||||
<h2 id="spec-url" class="scroll-mt-24">
|
||||
Specification URL
|
||||
</h2>
|
||||
<div class="my-6">
|
||||
<ul role="list"
|
||||
class="m-0 list-none divide-y divide-zinc-900/5 p-0 ">
|
||||
<li class="m-0 px-0 py-4 first:pt-0 last:pb-0">
|
||||
<dl class="m-0 flex flex-wrap items-center gap-x-3 gap-y-2">
|
||||
<dt class="sr-only">Endpoint</dt>
|
||||
<dd><code class="inline-code"><%= pageData.hostUrl %>api/openapi/spec</code></dd>
|
||||
<dt class="sr-only">Description</dt>
|
||||
<dd class="w-full flex-none [&>:first-child]:mt-0 [&>:last-child]:mb-0">
|
||||
<p>Complete OpenAPI 3.0 specification for the OneUptime API including all endpoints, request/response schemas, and authentication methods.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h2 id="benefits" class="scroll-mt-24">
|
||||
Benefits of Using the OpenAPI Spec
|
||||
</h2>
|
||||
<div class="my-6">
|
||||
<ul class="list-disc pl-6">
|
||||
<li class="mb-2"><strong>Interactive Documentation:</strong> Test API endpoints directly in Swagger Editor</li>
|
||||
<li class="mb-2"><strong>Code Generation:</strong> Generate client SDKs in multiple programming languages</li>
|
||||
<li class="mb-2"><strong>Validation:</strong> Ensure your requests match the expected schema</li>
|
||||
<li class="mb-2"><strong>Import to Tools:</strong> Use with Postman, Insomnia, or other API testing tools</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
@@ -3,23 +3,15 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:24.9-alpine3.21
|
||||
FROM node:21.7.3-alpine3.18
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
RUN npm config set fetch-retry-mintimeout 100000
|
||||
RUN npm config set fetch-retry-maxtimeout 600000
|
||||
|
||||
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
ARG IS_ENTERPRISE_EDITION=false
|
||||
|
||||
ENV GIT_SHA=${GIT_SHA}
|
||||
ENV APP_VERSION=${APP_VERSION}
|
||||
ENV IS_ENTERPRISE_EDITION=${IS_ENTERPRISE_EDITION}
|
||||
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
|
||||
|
||||
|
||||
# IF APP_VERSION is not set, set it to 1.0.0
|
||||
@@ -44,17 +36,33 @@ COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
# Set version in ./Model/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Model/package.json
|
||||
RUN npm install
|
||||
COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
# Set version in ./CommonServer/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/CommonServer/package.json
|
||||
RUN npm install
|
||||
COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
|
||||
|
||||
# Install CommonUI
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY ./CommonUI/package*.json /usr/src/CommonUI/
|
||||
# Set version in ./CommonUI/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/CommonUI/package.json
|
||||
RUN npm install --force
|
||||
COPY ./CommonUI /usr/src/CommonUI
|
||||
|
||||
|
||||
|
||||
@@ -72,11 +80,12 @@ RUN npm install
|
||||
# - 3003: accounts
|
||||
EXPOSE 3003
|
||||
|
||||
|
||||
RUN npm i -D webpack-cli
|
||||
|
||||
{{ if eq .Env.ENVIRONMENT "development" }}
|
||||
|
||||
|
||||
RUN mkdir /usr/src/app/dev-env
|
||||
RUN touch /usr/src/app/dev-env/.env
|
||||
RUN npm i -D webpack-dev-server
|
||||
|
||||
#Run the app
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
@@ -86,8 +95,6 @@ COPY ./Accounts /usr/src/app
|
||||
# Bundle app source
|
||||
|
||||
RUN npm run build
|
||||
# Set permission to write logs and cache in case container run as non root
|
||||
RUN chown -R 1000:1000 "/tmp/npm" && chmod -R 2777 "/tmp/npm"
|
||||
#Run the app
|
||||
CMD [ "npm", "start" ]
|
||||
{{ end }}
|
||||
|
||||
@@ -35,7 +35,7 @@ See the section about [deployment](https://facebook.github.io/create-react-app/d
|
||||
|
||||
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 ( 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.
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import Express, { ExpressApplication } from "Common/Server/Utils/Express";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import App from "Common/Server/Utils/StartServer";
|
||||
import "ejs";
|
||||
import Express, { ExpressApplication } from "CommonServer/Utils/Express";
|
||||
import logger from "CommonServer/Utils/Logger";
|
||||
import App from "CommonServer/Utils/StartServer";
|
||||
|
||||
export const APP_NAME: string = "accounts";
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
const { createConfig, build, watch } = require('Common/UI/esbuild-config');
|
||||
|
||||
const config = createConfig({
|
||||
serviceName: 'Accounts',
|
||||
publicPath: '/accounts/dist/',
|
||||
});
|
||||
|
||||
if (process.argv.includes('--watch')) {
|
||||
watch(config, 'Accounts');
|
||||
} else {
|
||||
build(config, 'Accounts');
|
||||
}
|
||||
@@ -1,17 +1,4 @@
|
||||
{
|
||||
"watch": ["./","../Common/UI", "../Common/Types", "../Common/Utils", "../Common/Models"],
|
||||
"ext": "ts,tsx",
|
||||
"ignore": [
|
||||
"./node_modules/**",
|
||||
"./public/**",
|
||||
"./bin/**",
|
||||
"./public/**",
|
||||
"./public/dist/**",
|
||||
"./build/*",
|
||||
"./build/**",
|
||||
"./build/dist/*",
|
||||
"./build/dist/**",
|
||||
"../Common/Server/**"
|
||||
],
|
||||
"exec": "npm run dev-build && npm run start"
|
||||
"watch": ["webpack.config.js"],
|
||||
"exec": "export DEBUG=express:* && printenv > /usr/src/app/dev-env/.env && echo 'USE_HTTPS=false' >> /usr/src/app/dev-env/.env && webpack-dev-server --port=3003 --mode=development"
|
||||
}
|
||||
2106
Accounts/package-lock.json
generated
2106
Accounts/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,8 @@
|
||||
"version": "0.1.0",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"dev-build": "NODE_ENV=development node esbuild.config.js",
|
||||
"dev": "npx nodemon",
|
||||
"build": "NODE_ENV=production node esbuild.config.js",
|
||||
"analyze": "analyze=true NODE_ENV=production node esbuild.config.js",
|
||||
"build": "webpack build --mode=production",
|
||||
"test": "",
|
||||
"compile": "tsc",
|
||||
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
|
||||
@@ -29,11 +27,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
|
||||
"ejs": "^3.1.10",
|
||||
"CommonServer": "file:../CommonServer",
|
||||
"CommonUI": "file:../CommonUI",
|
||||
"css-loader": "^6.11.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.30.1",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"sass-loader": "^13.3.3",
|
||||
"style-loader": "^3.3.4",
|
||||
"ts-loader": "^9.5.1",
|
||||
"use-async-effect": "^2.2.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -42,7 +48,7 @@
|
||||
"@types/react-dom": "^18.0.4",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"nodemon": "^2.0.20",
|
||||
|
||||
"ts-node": "^10.9.1"
|
||||
"ts-node": "^10.9.1",
|
||||
"webpack": "^5.76.0"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Accounts/public/assets/fonts/camphor/font1.woff2
Executable file
BIN
Accounts/public/assets/fonts/camphor/font1.woff2
Executable file
Binary file not shown.
BIN
Accounts/public/assets/fonts/camphor/font2.woff2
Executable file
BIN
Accounts/public/assets/fonts/camphor/font2.woff2
Executable file
Binary file not shown.
BIN
Accounts/public/assets/fonts/camphor/font3.woff2
Executable file
BIN
Accounts/public/assets/fonts/camphor/font3.woff2
Executable file
Binary file not shown.
BIN
Accounts/public/assets/fonts/camphor/font4.woff2
Executable file
BIN
Accounts/public/assets/fonts/camphor/font4.woff2
Executable file
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -12,7 +12,6 @@
|
||||
<meta name="slack-app-id" content="ACVBMTPJQ">
|
||||
<meta name="description" content="OneUptime — the complete open-source observability platform.">
|
||||
|
||||
<% if(typeof enableGoogleTagManager !== 'undefined' ? enableGoogleTagManager : false){ %>
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function (w, d, s, l, i) {
|
||||
w[l] = w[l] || []; w[l].push({
|
||||
@@ -23,7 +22,6 @@
|
||||
'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
|
||||
})(window, document, 'script', 'dataLayer', 'GTM-PKQD5WH');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
<% } %>
|
||||
|
||||
<link rel="manifest" href="/accounts/assets/img/favicons/ma">
|
||||
|
||||
@@ -75,11 +73,18 @@
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
<script src="/accounts/assets/js/tailwind-3.4.5.js"></script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
<title>OneUptime Accounts</title>
|
||||
|
||||
|
||||
<!-- Preload light, regular, medium and bold, which are fonts that are used on home -->
|
||||
<link rel="preload" href="/accounts/assets/fonts/camphor/font1.woff2" as="font" type="font/woff2" crossorigin="">
|
||||
<link rel="preload" href="/accounts/assets/fonts/camphor/font2.woff2" as="font" type="font/woff2" crossorigin="">
|
||||
<link rel="preload" href="/accounts/assets/fonts/camphor/font3.woff2" as="font" type="font/woff2" crossorigin="">
|
||||
<link rel="preload" href="/accounts/assets/fonts/camphor/font4.woff2" as="font" type="font/woff2" crossorigin="">
|
||||
|
||||
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
@@ -97,16 +102,14 @@
|
||||
</head>
|
||||
|
||||
<body class="h-full bg-gray-50">
|
||||
<% if(typeof enableGoogleTagManager !== 'undefined' ? enableGoogleTagManager : false){ %>
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PKQD5WH" height="0" width="0"
|
||||
style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
<% } %>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script type="module" src="/accounts/dist/Index.js"></script>
|
||||
<script src="/accounts/dist/bundle.js"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
@@ -1,4 +1,12 @@
|
||||
import React, { ReactElement, lazy, Suspense } from "react";
|
||||
import ForgotPasswordPage from "./Pages/ForgotPassword";
|
||||
import LoginPage from "./Pages/Login";
|
||||
import LoginWithSSO from "./Pages/LoginWithSSO";
|
||||
import NotFound from "./Pages/NotFound";
|
||||
import RegisterPage from "./Pages/Register";
|
||||
import ResetPasswordPage from "./Pages/ResetPassword";
|
||||
import VerifyEmail from "./Pages/VerifyEmail";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import React, { ReactElement } from "react";
|
||||
import {
|
||||
Route,
|
||||
Routes,
|
||||
@@ -6,38 +14,6 @@ import {
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
|
||||
// Lazy load page components
|
||||
const ForbiddenPage: React.LazyExoticComponent<() => JSX.Element> = lazy(() => {
|
||||
return import("./Pages/Forbidden");
|
||||
});
|
||||
const ForgotPasswordPage: React.LazyExoticComponent<() => JSX.Element> = lazy(
|
||||
() => {
|
||||
return import("./Pages/ForgotPassword");
|
||||
},
|
||||
);
|
||||
const LoginPage: React.LazyExoticComponent<() => JSX.Element> = lazy(() => {
|
||||
return import("./Pages/Login");
|
||||
});
|
||||
const LoginWithSSO: React.LazyExoticComponent<() => JSX.Element> = lazy(() => {
|
||||
return import("./Pages/LoginWithSSO");
|
||||
});
|
||||
const NotFound: React.LazyExoticComponent<() => JSX.Element> = lazy(() => {
|
||||
return import("./Pages/NotFound");
|
||||
});
|
||||
const RegisterPage: React.LazyExoticComponent<() => JSX.Element> = lazy(() => {
|
||||
return import("./Pages/Register");
|
||||
});
|
||||
const ResetPasswordPage: React.LazyExoticComponent<() => JSX.Element> = lazy(
|
||||
() => {
|
||||
return import("./Pages/ResetPassword");
|
||||
},
|
||||
);
|
||||
const VerifyEmail: React.LazyExoticComponent<() => JSX.Element> = lazy(() => {
|
||||
return import("./Pages/VerifyEmail");
|
||||
});
|
||||
|
||||
function App(): ReactElement {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
@@ -46,29 +22,24 @@ function App(): ReactElement {
|
||||
|
||||
return (
|
||||
<div className="m-auto h-screen">
|
||||
<Suspense fallback={<PageLoader isVisible={true} />}>
|
||||
<Routes>
|
||||
<Route path="/accounts" element={<LoginPage />} />
|
||||
<Route path="/accounts/login" element={<LoginPage />} />
|
||||
<Route path="/accounts/forbidden" element={<ForbiddenPage />} />
|
||||
<Route path="/accounts/sso" element={<LoginWithSSO />} />
|
||||
<Route
|
||||
path="/accounts/forgot-password"
|
||||
element={<ForgotPasswordPage />}
|
||||
/>
|
||||
<Route
|
||||
path="/accounts/reset-password/:token"
|
||||
element={<ResetPasswordPage />}
|
||||
/>
|
||||
<Route path="/accounts/register" element={<RegisterPage />} />
|
||||
<Route
|
||||
path="/accounts/verify-email/:token"
|
||||
element={<VerifyEmail />}
|
||||
/>
|
||||
{/* 👇️ only match this when no other routes match */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
<Routes>
|
||||
<Route path="/accounts" element={<LoginPage />} />
|
||||
<Route path="/accounts/login" element={<LoginPage />} />
|
||||
|
||||
<Route path="/accounts/sso" element={<LoginWithSSO />} />
|
||||
<Route
|
||||
path="/accounts/forgot-password"
|
||||
element={<ForgotPasswordPage />}
|
||||
/>
|
||||
<Route
|
||||
path="/accounts/reset-password/:token"
|
||||
element={<ResetPasswordPage />}
|
||||
/>
|
||||
<Route path="/accounts/register" element={<RegisterPage />} />
|
||||
<Route path="/accounts/verify-email/:token" element={<VerifyEmail />} />
|
||||
{/* 👇️ only match this when no other routes match */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import App from "./App";
|
||||
import Telemetry from "Common/UI/Utils/Telemetry/Telemetry";
|
||||
import Telemetry from "CommonUI/src/Utils/Telemetry";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
Telemetry.init({
|
||||
serviceName: "accounts",
|
||||
serviceName: "Accounts",
|
||||
});
|
||||
|
||||
const root: any = ReactDOM.createRoot(
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
const ForbiddenPage: () => JSX.Element = () => {
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Forbidden
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
You do not have permission to access this page.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForbiddenPage;
|
||||
@@ -1,11 +1,11 @@
|
||||
import { FORGOT_PASSWORD_API_URL } from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import ModelForm, { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import ModelForm, { FormType } from "CommonUI/src/Components/Forms/ModelForm";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "CommonUI/src/Components/Link/Link";
|
||||
import OneUptimeLogo from "CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import User from "Model/Models/User";
|
||||
import React, { useState } from "react";
|
||||
|
||||
const ForgotPassword: () => JSX.Element = () => {
|
||||
@@ -56,7 +56,6 @@ const ForgotPassword: () => JSX.Element = () => {
|
||||
title: "Email",
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
]}
|
||||
onSuccess={() => {
|
||||
|
||||
@@ -1,34 +1,19 @@
|
||||
import {
|
||||
LOGIN_API_URL,
|
||||
VERIFY_TOTP_AUTH_API_URL,
|
||||
GENERATE_WEBAUTHN_AUTH_OPTIONS_API_URL,
|
||||
VERIFY_WEBAUTHN_AUTH_API_URL,
|
||||
} from "../Utils/ApiPaths";
|
||||
import { LOGIN_API_URL } from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { JSONArray, JSONObject } from "Common/Types/JSON";
|
||||
import ModelForm, { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import { DASHBOARD_URL } from "Common/UI/Config";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import EditionLabel from "Common/UI/Components/EditionLabel/EditionLabel";
|
||||
import UiAnalytics from "Common/UI/Utils/Analytics";
|
||||
import LoginUtil from "Common/UI/Utils/Login";
|
||||
import UserTotpAuth from "Common/Models/DatabaseModels/UserTotpAuth";
|
||||
import UserWebAuthn from "Common/Models/DatabaseModels/UserWebAuthn";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import UserUtil from "Common/UI/Utils/User";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import ModelForm, { FormType } from "CommonUI/src/Components/Forms/ModelForm";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "CommonUI/src/Components/Link/Link";
|
||||
import { DASHBOARD_URL } from "CommonUI/src/Config";
|
||||
import OneUptimeLogo from "CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import UiAnalytics from "CommonUI/src/Utils/Analytics";
|
||||
import LoginUtil from "CommonUI/src/Utils/Login";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import UserUtil from "CommonUI/src/Utils/User";
|
||||
import User from "Model/Models/User";
|
||||
import React from "react";
|
||||
import useAsyncEffect from "use-async-effect";
|
||||
import BasicForm from "Common/UI/Components/Forms/BasicForm";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import Base64 from "Common/Utils/Base64";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import ComponentLoader from "Common/UI/Components/ComponentLoader/ComponentLoader";
|
||||
|
||||
const LoginPage: () => JSX.Element = () => {
|
||||
const apiUrl: URL = LOGIN_API_URL;
|
||||
@@ -39,40 +24,6 @@ const LoginPage: () => JSX.Element = () => {
|
||||
|
||||
const [initialValues, setInitialValues] = React.useState<JSONObject>({});
|
||||
|
||||
const [showTwoFactorAuth, setShowTwoFactorAuth] =
|
||||
React.useState<boolean>(false);
|
||||
|
||||
const [totpAuthList, setTotpAuthList] = React.useState<UserTotpAuth[]>([]);
|
||||
|
||||
const [webAuthnList, setWebAuthnList] = React.useState<UserWebAuthn[]>([]);
|
||||
|
||||
const [selectedTotpAuth, setSelectedTotpAuth] = React.useState<
|
||||
UserTotpAuth | undefined
|
||||
>(undefined);
|
||||
|
||||
const [selectedWebAuthn, setSelectedWebAuthn] = React.useState<
|
||||
UserWebAuthn | undefined
|
||||
>(undefined);
|
||||
|
||||
type TwoFactorMethod = {
|
||||
type: "totp" | "webauthn";
|
||||
item: UserTotpAuth | UserWebAuthn;
|
||||
};
|
||||
|
||||
const twoFactorMethods: TwoFactorMethod[] = [
|
||||
...totpAuthList.map((item: UserTotpAuth) => {
|
||||
return { type: "totp" as const, item };
|
||||
}),
|
||||
...webAuthnList.map((item: UserWebAuthn) => {
|
||||
return { type: "webauthn" as const, item };
|
||||
}),
|
||||
];
|
||||
|
||||
const [isTwoFactorAuthLoading, setIsTwoFactorAuthLoading] =
|
||||
React.useState<boolean>(false);
|
||||
const [twofactorAuthError, setTwoFactorAuthError] =
|
||||
React.useState<string>("");
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
if (Navigation.getQueryStringByName("email")) {
|
||||
setInitialValues({
|
||||
@@ -81,110 +32,6 @@ const LoginPage: () => JSX.Element = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
if (selectedWebAuthn) {
|
||||
setIsTwoFactorAuthLoading(true);
|
||||
try {
|
||||
const result: HTTPResponse<JSONObject> = await API.post({
|
||||
url: GENERATE_WEBAUTHN_AUTH_OPTIONS_API_URL,
|
||||
data: {
|
||||
data: {
|
||||
email: initialValues["email"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (result instanceof HTTPErrorResponse) {
|
||||
throw result;
|
||||
}
|
||||
|
||||
const data: any = result.data as any;
|
||||
|
||||
// Convert base64url strings back to Uint8Array
|
||||
data.options.challenge = Base64.base64UrlToUint8Array(
|
||||
data.options.challenge,
|
||||
);
|
||||
if (data.options.allowCredentials) {
|
||||
data.options.allowCredentials.forEach((cred: any) => {
|
||||
cred.id = Base64.base64UrlToUint8Array(cred.id);
|
||||
});
|
||||
}
|
||||
|
||||
// Use WebAuthn API
|
||||
const credential: PublicKeyCredential =
|
||||
(await navigator.credentials.get({
|
||||
publicKey: data.options,
|
||||
})) as PublicKeyCredential;
|
||||
|
||||
const assertionResponse: AuthenticatorAssertionResponse =
|
||||
credential.response as AuthenticatorAssertionResponse;
|
||||
|
||||
// Verify
|
||||
const verifyResult: HTTPResponse<JSONObject> = await API.post({
|
||||
url: VERIFY_WEBAUTHN_AUTH_API_URL,
|
||||
data: {
|
||||
data: {
|
||||
...initialValues,
|
||||
challenge: data.challenge,
|
||||
credential: {
|
||||
id: credential.id,
|
||||
rawId: Base64.uint8ArrayToBase64Url(
|
||||
new Uint8Array(credential.rawId),
|
||||
),
|
||||
response: {
|
||||
authenticatorData: Base64.uint8ArrayToBase64Url(
|
||||
new Uint8Array(assertionResponse.authenticatorData),
|
||||
),
|
||||
clientDataJSON: Base64.uint8ArrayToBase64Url(
|
||||
new Uint8Array(assertionResponse.clientDataJSON),
|
||||
),
|
||||
signature: Base64.uint8ArrayToBase64Url(
|
||||
new Uint8Array(assertionResponse.signature),
|
||||
),
|
||||
userHandle: assertionResponse.userHandle
|
||||
? Base64.uint8ArrayToBase64Url(
|
||||
new Uint8Array(assertionResponse.userHandle),
|
||||
)
|
||||
: null,
|
||||
},
|
||||
type: credential.type,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (verifyResult instanceof HTTPErrorResponse) {
|
||||
throw verifyResult;
|
||||
}
|
||||
|
||||
const user: User = User.fromJSON(
|
||||
verifyResult.data as JSONObject,
|
||||
User,
|
||||
) as User;
|
||||
const miscData: JSONObject = {};
|
||||
|
||||
login(user as User, miscData);
|
||||
} catch (error) {
|
||||
setTwoFactorAuthError(API.getFriendlyErrorMessage(error as Error));
|
||||
}
|
||||
setIsTwoFactorAuthLoading(false);
|
||||
}
|
||||
}, [selectedWebAuthn]);
|
||||
|
||||
type LoginFunction = (user: User, miscData: JSONObject) => void;
|
||||
|
||||
const login: LoginFunction = (user: User, miscData: JSONObject): void => {
|
||||
if (user instanceof User && user && user.email) {
|
||||
UiAnalytics.userAuth(user.email);
|
||||
UiAnalytics.capture("accounts/login");
|
||||
}
|
||||
|
||||
LoginUtil.login({
|
||||
user: user,
|
||||
token: miscData ? miscData["token"] : undefined,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
@@ -193,261 +40,89 @@ const LoginPage: () => JSX.Element = () => {
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<div className="mt-4 flex justify-center">
|
||||
<EditionLabel />
|
||||
</div>
|
||||
{!showTwoFactorAuth && (
|
||||
<>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Sign in to your account
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Join thousands of business that use OneUptime to help them stay
|
||||
online all the time.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{showTwoFactorAuth && (
|
||||
<>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Two Factor Authentication
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Select two factor authentication method. You will be asked to
|
||||
enter a code from the selected method.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Sign in to your account
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Join thousands of business that use OneUptime to help them stay online
|
||||
all the time.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
{!showTwoFactorAuth && (
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
id="login-form"
|
||||
name="Login"
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: "jeff@example.com",
|
||||
required: true,
|
||||
disabled: Boolean(initialValues && initialValues["email"]),
|
||||
title: "Email",
|
||||
dataTestId: "email",
|
||||
disableSpellCheck: true,
|
||||
<ModelForm<User>
|
||||
modelType={User}
|
||||
id="login-form"
|
||||
name="Login"
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
title: "Password",
|
||||
required: true,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
sideLink: {
|
||||
text: "Forgot password?",
|
||||
url: new Route("/accounts/forgot-password"),
|
||||
openLinkInNewTab: false,
|
||||
},
|
||||
dataTestId: "password",
|
||||
disableSpellCheck: true,
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: "jeff@example.com",
|
||||
required: true,
|
||||
disabled: Boolean(initialValues && initialValues["email"]),
|
||||
title: "Email",
|
||||
dataTestId: "email",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
password: true,
|
||||
},
|
||||
]}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
formType={FormType.Create}
|
||||
submitButtonText={"Login"}
|
||||
onBeforeCreate={(data: User) => {
|
||||
setInitialValues(User.toJSON(data, User));
|
||||
return Promise.resolve(data);
|
||||
}}
|
||||
onSuccess={(
|
||||
value: User | JSONObject,
|
||||
miscData: JSONObject | undefined,
|
||||
) => {
|
||||
if (
|
||||
miscData &&
|
||||
((((miscData as JSONObject)["totpAuthList"] as JSONArray)
|
||||
?.length || 0) > 0 ||
|
||||
(((miscData as JSONObject)["webAuthnList"] as JSONArray)
|
||||
?.length || 0) > 0)
|
||||
) {
|
||||
const totpAuthList: Array<UserTotpAuth> =
|
||||
UserTotpAuth.fromJSONArray(
|
||||
(miscData as JSONObject)["totpAuthList"] as JSONArray,
|
||||
UserTotpAuth,
|
||||
);
|
||||
const webAuthnList: Array<UserWebAuthn> =
|
||||
UserWebAuthn.fromJSONArray(
|
||||
(miscData as JSONObject)["webAuthnList"] as JSONArray,
|
||||
UserWebAuthn,
|
||||
);
|
||||
setTotpAuthList(totpAuthList);
|
||||
setWebAuthnList(webAuthnList);
|
||||
setShowTwoFactorAuth(true);
|
||||
return;
|
||||
}
|
||||
|
||||
login(value as User, miscData as JSONObject);
|
||||
}}
|
||||
maxPrimaryButtonWidth={true}
|
||||
footer={
|
||||
<div className="actions text-center mt-4 hover:underline fw-semibold">
|
||||
<div>
|
||||
<Link to={new Route("/accounts/sso")}>
|
||||
<div className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm">
|
||||
Use single sign-on (SSO) instead
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
title: "Password",
|
||||
required: true,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Password,
|
||||
sideLink: {
|
||||
text: "Forgot password?",
|
||||
url: new Route("/accounts/forgot-password"),
|
||||
openLinkInNewTab: false,
|
||||
},
|
||||
dataTestId: "password",
|
||||
},
|
||||
]}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
formType={FormType.Create}
|
||||
submitButtonText={"Login"}
|
||||
onSuccess={(value: User, miscData: JSONObject | undefined) => {
|
||||
if (value && value.email) {
|
||||
UiAnalytics.userAuth(value.email);
|
||||
UiAnalytics.capture("accounts/login");
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showTwoFactorAuth && !selectedTotpAuth && !selectedWebAuthn && (
|
||||
<div className="space-y-4">
|
||||
{twoFactorMethods.map(
|
||||
(method: TwoFactorMethod, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="cursor-pointer p-4 border border-gray-300 rounded-lg hover:bg-gray-50"
|
||||
onClick={() => {
|
||||
if (method.type === "totp") {
|
||||
setSelectedTotpAuth(method.item as UserTotpAuth);
|
||||
} else {
|
||||
setSelectedWebAuthn(method.item as UserWebAuthn);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="font-medium">
|
||||
{(method.item as any).name}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{method.type === "totp"
|
||||
? "Authenticator App"
|
||||
: "Security Key"}
|
||||
</div>
|
||||
LoginUtil.login({
|
||||
user: value,
|
||||
token: miscData ? miscData["token"] : undefined,
|
||||
});
|
||||
}}
|
||||
maxPrimaryButtonWidth={true}
|
||||
footer={
|
||||
<div className="actions text-center mt-4 hover:underline fw-semibold">
|
||||
<div>
|
||||
<Link to={new Route("/accounts/sso")}>
|
||||
<div className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm">
|
||||
Use single sign-on (SSO) instead
|
||||
</div>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showTwoFactorAuth && selectedWebAuthn && (
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-medium mb-4">
|
||||
Authenticating with Security Key
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 mb-4">
|
||||
Please follow the instructions on your security key device.
|
||||
</div>
|
||||
{isTwoFactorAuthLoading && <ComponentLoader />}
|
||||
{twofactorAuthError && (
|
||||
<ErrorMessage message={twofactorAuthError} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showTwoFactorAuth && selectedTotpAuth && (
|
||||
<BasicForm
|
||||
id="two-factor-auth-form"
|
||||
name="Two Factor Auth"
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
code: true,
|
||||
},
|
||||
title: "Code",
|
||||
description: "Enter the code from your authenticator app",
|
||||
required: true,
|
||||
dataTestId: "code",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
},
|
||||
]}
|
||||
submitButtonText={"Login"}
|
||||
maxPrimaryButtonWidth={true}
|
||||
isLoading={isTwoFactorAuthLoading}
|
||||
error={twofactorAuthError}
|
||||
onSubmit={async (data: JSONObject) => {
|
||||
setIsTwoFactorAuthLoading(true);
|
||||
|
||||
try {
|
||||
const code: string = data["code"] as string;
|
||||
const twoFactorAuthId: string =
|
||||
selectedTotpAuth!.id?.toString() as string;
|
||||
|
||||
const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post({
|
||||
url: VERIFY_TOTP_AUTH_API_URL,
|
||||
data: {
|
||||
data: {
|
||||
...initialValues,
|
||||
code: code,
|
||||
twoFactorAuthId: twoFactorAuthId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (result instanceof HTTPErrorResponse) {
|
||||
throw result;
|
||||
}
|
||||
|
||||
const user: User = User.fromJSON(
|
||||
result["data"] as JSONObject,
|
||||
User,
|
||||
) as User;
|
||||
const miscData: JSONObject = (result["data"] as JSONObject)[
|
||||
"miscData"
|
||||
] as JSONObject;
|
||||
|
||||
login(user as User, miscData as JSONObject);
|
||||
} catch (error) {
|
||||
setTwoFactorAuthError(
|
||||
API.getFriendlyErrorMessage(error as Error),
|
||||
);
|
||||
}
|
||||
|
||||
setIsTwoFactorAuthLoading(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-10 text-center">
|
||||
{!selectedTotpAuth && !selectedWebAuthn && (
|
||||
<div className="text-muted mb-0 text-gray-500">
|
||||
Don't have an account?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/register")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Register.
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{selectedTotpAuth || selectedWebAuthn ? (
|
||||
<div className="text-muted mb-0 text-gray-500">
|
||||
<Link
|
||||
onClick={() => {
|
||||
setSelectedTotpAuth(undefined);
|
||||
setSelectedWebAuthn(undefined);
|
||||
}}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Select a different two factor authentication method
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<div className="text-muted mb-0 text-gray-500">
|
||||
Don't have an account?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/register")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Register.
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,22 +2,22 @@ import { SERVICE_PROVIDER_LOGIN_URL } from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { JSONArray, JSONObject } from "Common/Types/JSON";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import { DASHBOARD_URL, IDENTITY_URL } from "Common/UI/Config";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import UserUtil from "Common/UI/Utils/User";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "CommonUI/src/Components/Link/Link";
|
||||
import { DASHBOARD_URL, IDENTITY_URL } from "CommonUI/src/Config";
|
||||
import OneUptimeLogo from "CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import UserUtil from "CommonUI/src/Utils/User";
|
||||
import User from "Model/Models/User";
|
||||
import React, { ReactElement, useState } from "react";
|
||||
import ProjectSSO from "Common/Models/DatabaseModels/ProjectSso";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import BasicForm from "Common/UI/Components/Forms/BasicForm";
|
||||
import ProjectSSO from "Model/Models/ProjectSso";
|
||||
import PageLoader from "CommonUI/src/Components/Loader/PageLoader";
|
||||
import API from "CommonUI/src/Utils/API/API";
|
||||
import BasicForm from "CommonUI/src/Components/Forms/BasicForm";
|
||||
import Email from "Common/Types/Email";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import StaticModelList from "Common/UI/Components/ModelList/StaticModelList";
|
||||
import StaticModelList from "CommonUI/src/Components/ModelList/StaticModelList";
|
||||
|
||||
const LoginPage: () => JSX.Element = () => {
|
||||
const apiUrl: URL = SERVICE_PROVIDER_LOGIN_URL;
|
||||
@@ -42,12 +42,12 @@ const LoginPage: () => JSX.Element = () => {
|
||||
try {
|
||||
// get sso config by email.
|
||||
const listResult: HTTPErrorResponse | HTTPResponse<JSONArray> =
|
||||
await API.get({
|
||||
url: URL.fromString(apiUrl.toString()).addQueryParam(
|
||||
await API.get(
|
||||
URL.fromString(apiUrl.toString()).addQueryParam(
|
||||
"email",
|
||||
email.toString(),
|
||||
),
|
||||
});
|
||||
);
|
||||
|
||||
if (listResult instanceof HTTPErrorResponse) {
|
||||
throw listResult;
|
||||
@@ -196,7 +196,6 @@ const LoginPage: () => JSX.Element = () => {
|
||||
required: true,
|
||||
title: "Email",
|
||||
dataTestId: "email",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
]}
|
||||
maxPrimaryButtonWidth={true}
|
||||
@@ -209,7 +208,7 @@ const LoginPage: () => JSX.Element = () => {
|
||||
<div>
|
||||
<Link to={new Route("/accounts/login")}>
|
||||
<div className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm">
|
||||
Use username and password instead.
|
||||
Use username and password insead.
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -3,23 +3,23 @@ import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import ModelForm, { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import Fields from "Common/UI/Components/Forms/Types/Fields";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import { BILLING_ENABLED, DASHBOARD_URL } from "Common/UI/Config";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import BaseAPI from "Common/UI/Utils/API/API";
|
||||
import UiAnalytics from "Common/UI/Utils/Analytics";
|
||||
import LocalStorage from "Common/UI/Utils/LocalStorage";
|
||||
import LoginUtil from "Common/UI/Utils/Login";
|
||||
import ModelAPI, { ListResult } from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import UserUtil from "Common/UI/Utils/User";
|
||||
import Reseller from "Common/Models/DatabaseModels/Reseller";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import ErrorMessage from "CommonUI/src/Components/ErrorMessage/ErrorMessage";
|
||||
import ModelForm, { FormType } from "CommonUI/src/Components/Forms/ModelForm";
|
||||
import Fields from "CommonUI/src/Components/Forms/Types/Fields";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "CommonUI/src/Components/Link/Link";
|
||||
import PageLoader from "CommonUI/src/Components/Loader/PageLoader";
|
||||
import { BILLING_ENABLED, DASHBOARD_URL } from "CommonUI/src/Config";
|
||||
import OneUptimeLogo from "CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import BaseAPI from "CommonUI/src/Utils/API/API";
|
||||
import UiAnalytics from "CommonUI/src/Utils/Analytics";
|
||||
import LocalStorage from "CommonUI/src/Utils/LocalStorage";
|
||||
import LoginUtil from "CommonUI/src/Utils/Login";
|
||||
import ModelAPI, { ListResult } from "CommonUI/src/Utils/ModelAPI/ModelAPI";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import UserUtil from "CommonUI/src/Utils/User";
|
||||
import Reseller from "Model/Models/Reseller";
|
||||
import User from "Model/Models/User";
|
||||
import React, { useState } from "react";
|
||||
import useAsyncEffect from "use-async-effect";
|
||||
|
||||
@@ -104,7 +104,6 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
disabled: Boolean(initialValues && initialValues["email"]),
|
||||
title: "Email",
|
||||
dataTestId: "email",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -115,7 +114,6 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
required: true,
|
||||
title: "Full Name",
|
||||
dataTestId: "name",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -130,7 +128,6 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
required: true,
|
||||
title: "Company Name",
|
||||
dataTestId: "companyName",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -162,7 +159,6 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
title: "Password",
|
||||
required: true,
|
||||
dataTestId: "password",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -179,12 +175,11 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
required: true,
|
||||
showEvenIfPermissionDoesNotExist: true,
|
||||
dataTestId: "confirmPassword",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
]);
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage message={error} />;
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { RESET_PASSWORD_API_URL } from "../Utils/ApiPaths";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import ModelForm, { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import ModelForm, { FormType } from "CommonUI/src/Components/Forms/ModelForm";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import Link from "CommonUI/src/Components/Link/Link";
|
||||
import OneUptimeLogo from "CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import User from "Model/Models/User";
|
||||
import React, { useState } from "react";
|
||||
|
||||
const RegisterPage: () => JSX.Element = () => {
|
||||
@@ -68,7 +68,6 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
title: "New Password",
|
||||
required: true,
|
||||
showEvenIfPermissionDoesNotExist: true,
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -84,7 +83,6 @@ const RegisterPage: () => JSX.Element = () => {
|
||||
overrideFieldKey: "confirmPassword",
|
||||
required: true,
|
||||
showEvenIfPermissionDoesNotExist: true,
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
]}
|
||||
createOrUpdateApiUrl={apiUrl}
|
||||
|
||||
@@ -3,14 +3,14 @@ import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import Link from "Common/UI/Components/Link/Link";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import EmailVerificationToken from "Common/Models/DatabaseModels/EmailVerificationToken";
|
||||
import { FormType } from "CommonUI/src/Components/Forms/ModelForm";
|
||||
import Link from "CommonUI/src/Components/Link/Link";
|
||||
import PageLoader from "CommonUI/src/Components/Loader/PageLoader";
|
||||
import OneUptimeLogo from "CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import API from "CommonUI/src/Utils/API/API";
|
||||
import ModelAPI from "CommonUI/src/Utils/ModelAPI/ModelAPI";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import EmailVerificationToken from "Model/Models/EmailVerificationToken";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
const VerifyEmail: () => JSX.Element = () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { IDENTITY_URL, APP_API_URL } from "Common/UI/Config";
|
||||
import { IDENTITY_URL } from "CommonUI/src/Config";
|
||||
|
||||
export const SIGNUP_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route("/signup"),
|
||||
@@ -9,18 +9,6 @@ export const LOGIN_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route("/login"),
|
||||
);
|
||||
|
||||
export const VERIFY_TOTP_AUTH_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route("/verify-totp-auth"),
|
||||
);
|
||||
|
||||
export const GENERATE_WEBAUTHN_AUTH_OPTIONS_API_URL: URL = URL.fromURL(
|
||||
APP_API_URL,
|
||||
).addRoute(new Route("/user-webauthn/generate-authentication-options"));
|
||||
|
||||
export const VERIFY_WEBAUTHN_AUTH_API_URL: URL = URL.fromURL(
|
||||
IDENTITY_URL,
|
||||
).addRoute(new Route("/verify-webauthn-auth"));
|
||||
|
||||
export const SERVICE_PROVIDER_LOGIN_URL: URL = URL.fromURL(
|
||||
IDENTITY_URL,
|
||||
).addRoute(new Route("/service-provider-login"));
|
||||
|
||||
84
Accounts/webpack.config.js
Normal file
84
Accounts/webpack.config.js
Normal file
@@ -0,0 +1,84 @@
|
||||
require("ts-loader");
|
||||
require("file-loader");
|
||||
require("style-loader");
|
||||
require("css-loader");
|
||||
require("sass-loader");
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const dotenv = require("dotenv");
|
||||
const express = require("express");
|
||||
|
||||
const readEnvFile = (pathToFile) => {
|
||||
const parsed = dotenv.config({ path: pathToFile }).parsed;
|
||||
|
||||
const env = {};
|
||||
|
||||
for (const key in parsed) {
|
||||
env[key] = JSON.stringify(parsed[key]);
|
||||
}
|
||||
|
||||
return env;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
entry: "./src/Index.tsx",
|
||||
mode: "development",
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
path: path.resolve(__dirname, "public", "dist"),
|
||||
publicPath: "/accounts/dist/",
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".tsx", ".js", ".jsx", ".json", ".css", ".scss"],
|
||||
alias: {
|
||||
react: path.resolve("./node_modules/react"),
|
||||
},
|
||||
},
|
||||
externals: {
|
||||
"react-native-sqlite-storage": "react-native-sqlite-storage",
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
process: {
|
||||
env: {
|
||||
...readEnvFile("/usr/src/app/dev-env/.env"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
use: "ts-loader",
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: ["style-loader", "css-loader", "sass-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
loader: "file-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
devMiddleware: {
|
||||
writeToDisk: true,
|
||||
},
|
||||
allowedHosts: "all",
|
||||
setupMiddlewares: (middlewares, devServer) => {
|
||||
devServer.app.use(
|
||||
"/accounts/assets",
|
||||
express.static(path.resolve(__dirname, "public", "assets")),
|
||||
);
|
||||
return middlewares;
|
||||
},
|
||||
},
|
||||
devtool: "eval-source-map",
|
||||
};
|
||||
@@ -3,23 +3,15 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:24.9-alpine3.21
|
||||
FROM node:21.7.3-alpine3.18
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
RUN npm config set fetch-retry-mintimeout 100000
|
||||
RUN npm config set fetch-retry-maxtimeout 600000
|
||||
|
||||
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
ARG IS_ENTERPRISE_EDITION=false
|
||||
|
||||
ENV GIT_SHA=${GIT_SHA}
|
||||
ENV APP_VERSION=${APP_VERSION}
|
||||
ENV IS_ENTERPRISE_EDITION=${IS_ENTERPRISE_EDITION}
|
||||
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
|
||||
|
||||
|
||||
# IF APP_VERSION is not set, set it to 1.0.0
|
||||
@@ -43,17 +35,33 @@ RUN npm install
|
||||
COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
# Set version in ./Model/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Model/package.json
|
||||
RUN npm install
|
||||
COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
# Set version in ./CommonServer/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/CommonServer/package.json
|
||||
RUN npm install
|
||||
COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
|
||||
|
||||
# Install CommonUI
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY ./CommonUI/package*.json /usr/src/CommonUI/
|
||||
# Set version in ./CommonUI/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/CommonUI/package.json
|
||||
RUN npm install --force
|
||||
COPY ./CommonUI /usr/src/CommonUI
|
||||
|
||||
|
||||
|
||||
@@ -70,20 +78,19 @@ RUN npm install
|
||||
# - 3158: AdminDashboard
|
||||
EXPOSE 3158
|
||||
|
||||
|
||||
RUN npm i -D webpack-cli
|
||||
|
||||
{{ if eq .Env.ENVIRONMENT "development" }}
|
||||
#Run the app
|
||||
|
||||
|
||||
RUN mkdir /usr/src/app/dev-env
|
||||
RUN touch /usr/src/app/dev-env/.env
|
||||
RUN npm i -D webpack-dev-server
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
{{ else }}
|
||||
# Copy app source
|
||||
COPY ./AdminDashboard /usr/src/app
|
||||
# Bundle app source
|
||||
RUN npm run build
|
||||
# Set permission to write logs and cache in case container run as non root
|
||||
RUN chown -R 1000:1000 "/tmp/npm" && chmod -R 2777 "/tmp/npm"
|
||||
#Run the app
|
||||
CMD [ "npm", "start" ]
|
||||
{{ end }}
|
||||
|
||||
@@ -35,7 +35,7 @@ See the section about [deployment](https://facebook.github.io/create-react-app/d
|
||||
|
||||
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 ( 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.
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,74 +1,12 @@
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import Express, {
|
||||
ExpressApplication,
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
} from "Common/Server/Utils/Express";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import App from "Common/Server/Utils/StartServer";
|
||||
import Response from "Common/Server/Utils/Response";
|
||||
import UserMiddleware from "Common/Server/Middleware/UserAuthorization";
|
||||
import JSONWebToken from "Common/Server/Utils/JsonWebToken";
|
||||
import NotAuthorizedException from "Common/Types/Exception/NotAuthorizedException";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import "ejs";
|
||||
import JSONWebTokenData from "Common/Types/JsonWebTokenData";
|
||||
import Express, { ExpressApplication } from "CommonServer/Utils/Express";
|
||||
import logger from "CommonServer/Utils/Logger";
|
||||
import App from "CommonServer/Utils/StartServer";
|
||||
|
||||
export const APP_NAME: string = "admin";
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
type EnsureMasterAdminAccessFunction = (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
) => Promise<JSONObject>;
|
||||
|
||||
const ensureMasterAdminAccess: EnsureMasterAdminAccessFunction = async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<JSONObject> => {
|
||||
try {
|
||||
const accessToken: string | undefined =
|
||||
UserMiddleware.getAccessTokenFromExpressRequest(req);
|
||||
|
||||
if (!accessToken) {
|
||||
Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthorizedException(
|
||||
"Unauthorized: Only master admins can access the admin dashboard.",
|
||||
),
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
const authData: JSONWebTokenData = JSONWebToken.decode(accessToken);
|
||||
|
||||
if (!authData.isMasterAdmin) {
|
||||
Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthorizedException(
|
||||
"Unauthorized: Only master admins can access the admin dashboard.",
|
||||
),
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
return {};
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthorizedException(
|
||||
"Unauthorized: Only master admins can access the admin dashboard.",
|
||||
),
|
||||
);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
// init the app
|
||||
@@ -80,7 +18,6 @@ const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
liveCheck: async () => {},
|
||||
readyCheck: async () => {},
|
||||
},
|
||||
getVariablesToRenderIndexPage: ensureMasterAdminAccess,
|
||||
});
|
||||
|
||||
// add default routes
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
const { createConfig, build, watch } = require('Common/UI/esbuild-config');
|
||||
|
||||
const config = createConfig({
|
||||
serviceName: 'AdminDashboard',
|
||||
publicPath: '/admin/dist/',
|
||||
});
|
||||
|
||||
if (process.argv.includes('--watch')) {
|
||||
watch(config, 'AdminDashboard');
|
||||
} else {
|
||||
build(config, 'AdminDashboard');
|
||||
}
|
||||
@@ -1,17 +1,4 @@
|
||||
{
|
||||
"watch": ["./","../Common/UI", "../Common/Types", "../Common/Utils", "../Common/Models"],
|
||||
"ext": "ts,tsx",
|
||||
"ignore": [
|
||||
"./node_modules/**",
|
||||
"./public/**",
|
||||
"./bin/**",
|
||||
"./public/**",
|
||||
"./public/dist/**",
|
||||
"./build/*",
|
||||
"./build/**",
|
||||
"./build/dist/*",
|
||||
"./build/dist/**",
|
||||
"../Common/Server/**"
|
||||
],
|
||||
"exec": " npm run dev-build && npm run start"
|
||||
"watch": ["webpack.config.js"],
|
||||
"exec": "export DEBUG=express:* && printenv > /usr/src/app/dev-env/.env && echo 'USE_HTTPS=false' >> /usr/src/app/dev-env/.env && webpack-dev-server --port=3158 --mode=development"
|
||||
}
|
||||
20159
AdminDashboard/package-lock.json
generated
20159
AdminDashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,19 +4,21 @@
|
||||
"private": false,
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
|
||||
"ejs": "^3.1.10",
|
||||
"CommonServer": "file:../CommonServer",
|
||||
"CommonUI": "file:../CommonUI",
|
||||
"dotenv": "^16.4.5",
|
||||
"file-loader": "^6.2.0",
|
||||
"Model": "file:../Model",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.30.1"
|
||||
"react-router-dom": "^6.23.1",
|
||||
"style-loader": "^3.3.4"
|
||||
},
|
||||
"scripts": {
|
||||
"dev-build": "NODE_ENV=development node esbuild.config.js",
|
||||
"dev": "npx nodemon",
|
||||
"build": "NODE_ENV=production node esbuild.config.js",
|
||||
"analyze": "analyze=true NODE_ENV=production node esbuild.config.js",
|
||||
"build": "webpack build --mode=production",
|
||||
"test": "react-app-rewired test",
|
||||
"eject": "echo 'esbuild does not require eject'",
|
||||
"eject": "webpack eject",
|
||||
"compile": "tsc",
|
||||
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
|
||||
"start": "node --require ts-node/register Serve.ts",
|
||||
@@ -41,8 +43,13 @@
|
||||
"@types/react": "^18.2.38",
|
||||
"@types/react-dom": "^18.0.4",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"css-loader": "^6.8.1",
|
||||
"nodemon": "^2.0.20",
|
||||
|
||||
"ts-node": "^10.9.1"
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"sass": "^1.51.0",
|
||||
"sass-loader": "^12.6.0",
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"webpack": "^5.76.0"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
AdminDashboard/public/assets/fonts/camphor/font1.woff2
Executable file
BIN
AdminDashboard/public/assets/fonts/camphor/font1.woff2
Executable file
Binary file not shown.
BIN
AdminDashboard/public/assets/fonts/camphor/font2.woff2
Executable file
BIN
AdminDashboard/public/assets/fonts/camphor/font2.woff2
Executable file
Binary file not shown.
BIN
AdminDashboard/public/assets/fonts/camphor/font3.woff2
Executable file
BIN
AdminDashboard/public/assets/fonts/camphor/font3.woff2
Executable file
Binary file not shown.
BIN
AdminDashboard/public/assets/fonts/camphor/font4.woff2
Executable file
BIN
AdminDashboard/public/assets/fonts/camphor/font4.woff2
Executable file
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -11,7 +11,6 @@
|
||||
<meta name="slack-app-id" content="ACVBMTPJQ">
|
||||
<meta name="description" content="OneUptime — the complete open-source observability platform.">
|
||||
|
||||
<% if(typeof enableGoogleTagManager !== 'undefined' ? enableGoogleTagManager : false){ %>
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
@@ -20,7 +19,6 @@
|
||||
})(window,document,'script','dataLayer','GTM-PKQD5WH');
|
||||
</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
<% } %>
|
||||
|
||||
<link rel="manifest" href="/admin/assets/img/favicons/ma">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/admin/assets/img/favicons/apple-touch-icon.png">
|
||||
@@ -68,13 +66,20 @@
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
<script src="/admin/assets/js/tailwind-3.4.5.js"></script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
<script src="/admin/env.js"></script>
|
||||
|
||||
|
||||
<title>OneUptime Admin Dashboard</title>
|
||||
|
||||
|
||||
<!-- Preload light, regular, medium and bold, which are fonts that are used on home
|
||||
<link rel="preload" href="/admin/assets/fonts/camphor/font1.woff2" as="font" type="font/woff2" crossorigin="">
|
||||
<link rel="preload" href="/admin/assets/fonts/camphor/font2.woff2" as="font" type="font/woff2" crossorigin="">
|
||||
<link rel="preload" href="/admin/assets/fonts/camphor/font3.woff2" as="font" type="font/woff2" crossorigin="">
|
||||
<link rel="preload" href="/admin/assets/fonts/camphor/font4.woff2" as="font" type="font/woff2" crossorigin=""> -->
|
||||
|
||||
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
@@ -91,16 +96,14 @@
|
||||
-->
|
||||
</head>
|
||||
<body class="h-full bg-gray-50">
|
||||
<% if(typeof enableGoogleTagManager !== 'undefined' ? enableGoogleTagManager : false){ %>
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PKQD5WH"
|
||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
<% } %>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script type="module" src="/admin/dist/Index.js"></script>
|
||||
<script src="/admin/dist/bundle.js"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
@@ -5,7 +5,6 @@ import Projects from "./Pages/Projects/Index";
|
||||
import SettingsAPIKey from "./Pages/Settings/APIKey/Index";
|
||||
import SettingsAuthentication from "./Pages/Settings/Authentication/Index";
|
||||
import SettingsCallSMS from "./Pages/Settings/CallSMS/Index";
|
||||
import SettingsWhatsApp from "./Pages/Settings/WhatsApp/Index";
|
||||
// Settings Pages.
|
||||
import SettingsEmail from "./Pages/Settings/Email/Index";
|
||||
import SettingsProbes from "./Pages/Settings/Probes/Index";
|
||||
@@ -13,9 +12,9 @@ import Users from "./Pages/Users/Index";
|
||||
import PageMap from "./Utils/PageMap";
|
||||
import RouteMap from "./Utils/RouteMap";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { ACCOUNTS_URL, DASHBOARD_URL } from "Common/UI/Config";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import User from "Common/UI/Utils/User";
|
||||
import { ACCOUNTS_URL, DASHBOARD_URL } from "CommonUI/src/Config";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import User from "CommonUI/src/Utils/User";
|
||||
import React from "react";
|
||||
import {
|
||||
Route as PageRoute,
|
||||
@@ -24,11 +23,6 @@ import {
|
||||
useNavigate,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import UserView from "./Pages/Users/View/Index";
|
||||
import UserDelete from "./Pages/Users/View/Delete";
|
||||
import UserSettings from "./Pages/Users/View/Settings";
|
||||
import ProjectView from "./Pages/Projects/View/Index";
|
||||
import ProjectDelete from "./Pages/Projects/View/Delete";
|
||||
|
||||
const App: () => JSX.Element = () => {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
@@ -67,31 +61,6 @@ const App: () => JSX.Element = () => {
|
||||
element={<Users />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.USER_VIEW]?.toString() || ""}
|
||||
element={<UserView />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.USER_SETTINGS]?.toString() || ""}
|
||||
element={<UserSettings />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.USER_DELETE]?.toString() || ""}
|
||||
element={<UserDelete />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.PROJECT_VIEW]?.toString() || ""}
|
||||
element={<ProjectView />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.PROJECT_DELETE]?.toString() || ""}
|
||||
element={<ProjectDelete />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.LOGOUT]?.toString() || ""}
|
||||
element={<Logout />}
|
||||
@@ -112,11 +81,6 @@ const App: () => JSX.Element = () => {
|
||||
element={<SettingsCallSMS />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_WHATSAPP]?.toString() || ""}
|
||||
element={<SettingsWhatsApp />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_PROBES]?.toString() || ""}
|
||||
element={<SettingsProbes />}
|
||||
|
||||
@@ -5,9 +5,9 @@ import BadDataException from "Common/Types/Exception/BadDataException";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import API from "Common/Utils/API";
|
||||
import Footer from "Common/UI/Components/Footer/Footer";
|
||||
import ConfirmModal from "Common/UI/Components/Modal/ConfirmModal";
|
||||
import { HOST, HTTP_PROTOCOL } from "Common/UI/Config";
|
||||
import Footer from "CommonUI/src/Components/Footer/Footer";
|
||||
import ConfirmModal from "CommonUI/src/Components/Modal/ConfirmModal";
|
||||
import { HOST, HTTP_PROTOCOL } from "CommonUI/src/Config";
|
||||
import React from "react";
|
||||
|
||||
const DashboardFooter: () => JSX.Element = () => {
|
||||
@@ -54,9 +54,9 @@ const DashboardFooter: () => JSX.Element = () => {
|
||||
const fetchAppVersion: (appName: string) => Promise<JSONObject> = async (
|
||||
appName: string,
|
||||
): Promise<JSONObject> => {
|
||||
const response: HTTPResponse<JSONObject> = await API.get<JSONObject>({
|
||||
url: URL.fromString(`${HTTP_PROTOCOL}/${HOST}${appName}/version`),
|
||||
});
|
||||
const response: HTTPResponse<JSONObject> = await API.get<JSONObject>(
|
||||
URL.fromString(`${HTTP_PROTOCOL}/${HOST}${appName}/version`),
|
||||
);
|
||||
|
||||
if (response.data) {
|
||||
return response.data as JSONObject;
|
||||
@@ -67,7 +67,7 @@ const DashboardFooter: () => JSX.Element = () => {
|
||||
return (
|
||||
<>
|
||||
<Footer
|
||||
className="bg-white px-8"
|
||||
className="bg-white h-16 inset-x-0 bottom-0 px-8"
|
||||
copyright="HackerBay, Inc."
|
||||
links={[
|
||||
{
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import Help from "./Help";
|
||||
import Logo from "./Logo";
|
||||
import UserProfile from "./UserProfile";
|
||||
import Button, { ButtonStyleType } from "Common/UI/Components/Button/Button";
|
||||
import Header from "Common/UI/Components/Header/Header";
|
||||
import EditionLabel from "Common/UI/Components/EditionLabel/EditionLabel";
|
||||
import { DASHBOARD_URL } from "Common/UI/Config";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import Button, { ButtonStyleType } from "CommonUI/src/Components/Button/Button";
|
||||
import Header from "CommonUI/src/Components/Header/Header";
|
||||
import { DASHBOARD_URL } from "CommonUI/src/Config";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const DashboardHeader: FunctionComponent = (): ReactElement => {
|
||||
@@ -28,7 +27,6 @@ const DashboardHeader: FunctionComponent = (): ReactElement => {
|
||||
}
|
||||
rightComponents={
|
||||
<>
|
||||
<EditionLabel className="mr-3 hidden md:inline-flex" />
|
||||
<Button
|
||||
title="Exit Admin"
|
||||
buttonStyle={ButtonStyleType.NORMAL}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import URL from "Common/Types/API/URL";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import HeaderIconDropdownButton from "Common/UI/Components/Header/HeaderIconDropdownButton";
|
||||
import IconDropdownItem from "Common/UI/Components/Header/IconDropdown/IconDropdownItem";
|
||||
import IconDropdownMenu from "Common/UI/Components/Header/IconDropdown/IconDropdownMenu";
|
||||
import IconDropdownRow from "Common/UI/Components/Header/IconDropdown/IconDropdownRow";
|
||||
import HeaderIconDropdownButton from "CommonUI/src/Components/Header/HeaderIconDropdownButton";
|
||||
import IconDropdownItem from "CommonUI/src/Components/Header/IconDropdown/IconDropdownItem";
|
||||
import IconDropdownMenu from "CommonUI/src/Components/Header/IconDropdown/IconDropdownMenu";
|
||||
import IconDropdownRow from "CommonUI/src/Components/Header/IconDropdown/IconDropdownRow";
|
||||
import React, { ReactElement, useState } from "react";
|
||||
|
||||
const Help: () => JSX.Element = (): ReactElement => {
|
||||
@@ -37,7 +37,7 @@ const Help: () => JSX.Element = (): ReactElement => {
|
||||
setIsDropdownVisible(false);
|
||||
}}
|
||||
url={URL.fromString(
|
||||
"https://join.slack.com/t/oneuptimesupport/shared_invite/zt-2pz5p1uhe-Fpmc7bv5ZE5xRMe7qJnwmA",
|
||||
"https://join.slack.com/t/oneuptimesupport/shared_invite/zt-1kavkds2f-gegm_wePorvwvM3M_SaoCQ",
|
||||
)}
|
||||
/>
|
||||
<IconDropdownItem
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Tailwind
|
||||
import Route from "Common/Types/API/Route";
|
||||
import Image from "Common/UI/Components/Image/Image";
|
||||
import OneUptimeLogo from "Common/UI/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import Image from "CommonUI/src/Components/Image/Image";
|
||||
import OneUptimeLogo from "CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.svg";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
export interface ComponentProps {
|
||||
@@ -17,9 +17,7 @@ const Logo: FunctionComponent<ComponentProps> = (
|
||||
<Image
|
||||
className="block h-8 w-auto"
|
||||
onClick={() => {
|
||||
if (props.onClick) {
|
||||
props.onClick();
|
||||
}
|
||||
props.onClick && props.onClick();
|
||||
}}
|
||||
imageUrl={Route.fromString(`${OneUptimeLogo}`)}
|
||||
alt={"OneUptime"}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import HeaderIconDropdownButton from "Common/UI/Components/Header/HeaderIconDropdownButton";
|
||||
import NotificationItem from "Common/UI/Components/Header/Notifications/NotificationItem";
|
||||
import Notifications from "Common/UI/Components/Header/Notifications/Notifications";
|
||||
import HeaderIconDropdownButton from "CommonUI/src/Components/Header/HeaderIconDropdownButton";
|
||||
import NotificationItem from "CommonUI/src/Components/Header/Notifications/NotificationItem";
|
||||
import Notifications from "CommonUI/src/Components/Header/Notifications/Notifications";
|
||||
import React, { ReactElement, useState } from "react";
|
||||
|
||||
const DashboardHeader: () => JSX.Element = (): ReactElement => {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import SubscriptionPlan from "Common/Types/Billing/SubscriptionPlan";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import Field from "Common/UI/Components/Forms/Types/Field";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ProjectPicker from "Common/UI/Components/Header/ProjectPicker/ProjectPicker";
|
||||
import ModelFormModal from "Common/UI/Components/ModelFormModal/ModelFormModal";
|
||||
import { RadioButton } from "Common/UI/Components/RadioButtons/GroupRadioButtons";
|
||||
import Toggle from "Common/UI/Components/Toggle/Toggle";
|
||||
import { BILLING_ENABLED, getAllEnvVars } from "Common/UI/Config";
|
||||
import { GetReactElementFunction } from "Common/UI/Types/FunctionTypes";
|
||||
import ProjectUtil from "Common/UI/Utils/Project";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import { FormType } from "CommonUI/src/Components/Forms/ModelForm";
|
||||
import Field from "CommonUI/src/Components/Forms/Types/Field";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ProjectPicker from "CommonUI/src/Components/Header/ProjectPicker/ProjectPicker";
|
||||
import ModelFormModal from "CommonUI/src/Components/ModelFormModal/ModelFormModal";
|
||||
import { RadioButton } from "CommonUI/src/Components/RadioButtons/GroupRadioButtons";
|
||||
import Toggle from "CommonUI/src/Components/Toggle/Toggle";
|
||||
import { BILLING_ENABLED, getAllEnvVars } from "CommonUI/src/Config";
|
||||
import { GetReactElementFunction } from "CommonUI/src/Types/FunctionTypes";
|
||||
import ProjectUtil from "CommonUI/src/Utils/Project";
|
||||
import Project from "Model/Models/Project";
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
@@ -137,7 +137,7 @@ const DashboardProjectPicker: FunctionComponent<ComponentProps> = (
|
||||
minLength: 6,
|
||||
},
|
||||
footerElement: getFooter(),
|
||||
fieldType: FormFieldSchemaType.OptionChooserButton,
|
||||
fieldType: FormFieldSchemaType.RadioButton,
|
||||
radioButtonOptions: SubscriptionPlan.getSubscriptionPlans(
|
||||
getAllEnvVars(),
|
||||
).map((plan: SubscriptionPlan): RadioButton => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import SearchBox from "Common/UI/Components/Header/SearchBox";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import SearchBox from "CommonUI/src/Components/Header/SearchBox";
|
||||
import Project from "Model/Models/Project";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
export interface ComponentProps {
|
||||
|
||||
@@ -2,13 +2,13 @@ import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import HeaderIconDropdownButton from "Common/UI/Components/Header/HeaderIconDropdownButton";
|
||||
import IconDropdownItem from "Common/UI/Components/Header/IconDropdown/IconDropdownItem";
|
||||
import IconDropdownMenu from "Common/UI/Components/Header/IconDropdown/IconDropdownMenu";
|
||||
import { DASHBOARD_URL } from "Common/UI/Config";
|
||||
import BlankProfilePic from "Common/UI/Images/users/blank-profile.svg";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import User from "Common/UI/Utils/User";
|
||||
import HeaderIconDropdownButton from "CommonUI/src/Components/Header/HeaderIconDropdownButton";
|
||||
import IconDropdownItem from "CommonUI/src/Components/Header/IconDropdown/IconDropdownItem";
|
||||
import IconDropdownMenu from "CommonUI/src/Components/Header/IconDropdown/IconDropdownMenu";
|
||||
import { DASHBOARD_URL } from "CommonUI/src/Config";
|
||||
import BlankProfilePic from "CommonUI/src/Images/users/blank-profile.svg";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import User from "CommonUI/src/Utils/User";
|
||||
import React, { FunctionComponent, ReactElement, useState } from "react";
|
||||
|
||||
const DashboardUserProfile: FunctionComponent = (): ReactElement => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Footer from "../Footer/Footer";
|
||||
import Header from "../Header/Header";
|
||||
import NavBar from "../NavBar/NavBar";
|
||||
import MasterPage from "Common/UI/Components/MasterPage/MasterPage";
|
||||
import TopAlert from "Common/UI/Components/TopAlert/TopAlert";
|
||||
import MasterPage from "CommonUI/src/Components/MasterPage/MasterPage";
|
||||
import TopAlert from "CommonUI/src/Components/TopAlert/TopAlert";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
export interface ComponentProps {
|
||||
|
||||
@@ -2,33 +2,36 @@ import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import NavBar, { NavItem } from "Common/UI/Components/Navbar/NavBar";
|
||||
import NavBar from "CommonUI/src/Components/Navbar/NavBar";
|
||||
import NavBarItem from "CommonUI/src/Components/Navbar/NavBarItem";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const DashboardNavbar: FunctionComponent = (): ReactElement => {
|
||||
// Build the navigation items
|
||||
const navItems: NavItem[] = [
|
||||
{
|
||||
id: "users-nav-bar-item",
|
||||
title: "Users",
|
||||
icon: IconProp.User,
|
||||
route: RouteUtil.populateRouteParams(RouteMap[PageMap.USERS] as Route),
|
||||
},
|
||||
{
|
||||
id: "projects-nav-bar-item",
|
||||
title: "Projects",
|
||||
icon: IconProp.Folder,
|
||||
route: RouteUtil.populateRouteParams(RouteMap[PageMap.PROJECTS] as Route),
|
||||
},
|
||||
{
|
||||
id: "settings-nav-bar-item",
|
||||
title: "Settings",
|
||||
icon: IconProp.Settings,
|
||||
route: RouteUtil.populateRouteParams(RouteMap[PageMap.SETTINGS] as Route),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<NavBar>
|
||||
<NavBarItem
|
||||
title="Users"
|
||||
icon={IconProp.User}
|
||||
route={RouteUtil.populateRouteParams(RouteMap[PageMap.USERS] as Route)}
|
||||
></NavBarItem>
|
||||
|
||||
return <NavBar items={navItems} />;
|
||||
<NavBarItem
|
||||
title="Projects"
|
||||
icon={IconProp.Folder}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECTS] as Route,
|
||||
)}
|
||||
></NavBarItem>
|
||||
|
||||
<NavBarItem
|
||||
title="Settings"
|
||||
icon={IconProp.Settings}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route,
|
||||
)}
|
||||
></NavBarItem>
|
||||
</NavBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardNavbar;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import App from "./App";
|
||||
import Telemetry from "Common/UI/Utils/Telemetry/Telemetry";
|
||||
import Telemetry from "CommonUI/src/Utils/Telemetry";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
Telemetry.init({
|
||||
serviceName: "admin-dashboard",
|
||||
serviceName: "AdminDashboard",
|
||||
});
|
||||
|
||||
const root: any = ReactDOM.createRoot(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap from "../../Utils/RouteMap";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import PageLoader from "CommonUI/src/Components/Loader/PageLoader";
|
||||
import Page from "CommonUI/src/Components/Page/Page";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
||||
|
||||
const Init: FunctionComponent = (): ReactElement => {
|
||||
|
||||
@@ -2,13 +2,13 @@ import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import { ACCOUNTS_URL } from "Common/UI/Config";
|
||||
import UiAnalytics from "Common/UI/Utils/Analytics";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import UserUtil from "Common/UI/Utils/User";
|
||||
import ErrorMessage from "CommonUI/src/Components/ErrorMessage/ErrorMessage";
|
||||
import PageLoader from "CommonUI/src/Components/Loader/PageLoader";
|
||||
import Page from "CommonUI/src/Components/Page/Page";
|
||||
import { ACCOUNTS_URL } from "CommonUI/src/Config";
|
||||
import UiAnalytics from "CommonUI/src/Utils/Analytics";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import UserUtil from "CommonUI/src/Utils/User";
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
||||
|
||||
const Logout: FunctionComponent = (): ReactElement => {
|
||||
@@ -16,7 +16,7 @@ const Logout: FunctionComponent = (): ReactElement => {
|
||||
|
||||
const logout: PromiseVoidFunction = async (): Promise<void> => {
|
||||
UiAnalytics.logout();
|
||||
UserUtil.logout();
|
||||
await UserUtil.logout();
|
||||
Navigation.navigate(ACCOUNTS_URL);
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ const Logout: FunctionComponent = (): ReactElement => {
|
||||
]}
|
||||
>
|
||||
{!error ? <PageLoader isVisible={true} /> : <></>}
|
||||
{error ? <ErrorMessage message={error} /> : <></>}
|
||||
{error ? <ErrorMessage error={error} /> : <></>}
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,18 +3,18 @@ import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import SubscriptionPlan from "Common/Types/Billing/SubscriptionPlan";
|
||||
import Field from "Common/UI/Components/Forms/Types/Field";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ModelTable from "Common/UI/Components/ModelTable/ModelTable";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import { RadioButton } from "Common/UI/Components/RadioButtons/GroupRadioButtons";
|
||||
import Toggle from "Common/UI/Components/Toggle/Toggle";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import { BILLING_ENABLED, getAllEnvVars } from "Common/UI/Config";
|
||||
import { GetReactElementFunction } from "Common/UI/Types/FunctionTypes";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import Field from "CommonUI/src/Components/Forms/Types/Field";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ModelTable from "CommonUI/src/Components/ModelTable/ModelTable";
|
||||
import Page from "CommonUI/src/Components/Page/Page";
|
||||
import { RadioButton } from "CommonUI/src/Components/RadioButtons/GroupRadioButtons";
|
||||
import Toggle from "CommonUI/src/Components/Toggle/Toggle";
|
||||
import FieldType from "CommonUI/src/Components/Types/FieldType";
|
||||
import { BILLING_ENABLED, getAllEnvVars } from "CommonUI/src/Config";
|
||||
import { GetReactElementFunction } from "CommonUI/src/Types/FunctionTypes";
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import Project from "Model/Models/Project";
|
||||
import User from "Model/Models/User";
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
@@ -75,7 +75,7 @@ const Projects: FunctionComponent = (): ReactElement => {
|
||||
minLength: 6,
|
||||
},
|
||||
footerElement: getFooter(),
|
||||
fieldType: FormFieldSchemaType.OptionChooserButton,
|
||||
fieldType: FormFieldSchemaType.RadioButton,
|
||||
radioButtonOptions: SubscriptionPlan.getSubscriptionPlans(
|
||||
getAllEnvVars(),
|
||||
).map((plan: SubscriptionPlan): RadioButton => {
|
||||
@@ -187,7 +187,7 @@ const Projects: FunctionComponent = (): ReactElement => {
|
||||
isEditable={false}
|
||||
isCreateable={true}
|
||||
name="Projects"
|
||||
isViewable={true}
|
||||
isViewable={false}
|
||||
cardProps={{
|
||||
title: "Projects",
|
||||
description: "Here is a list of proejcts in OneUptime.",
|
||||
@@ -241,10 +241,8 @@ const Projects: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: "Created At",
|
||||
type: FieldType.DateTime,
|
||||
hideOnMobile: true,
|
||||
},
|
||||
]}
|
||||
userPreferencesKey="admin-projects-table"
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import ModelDelete from "Common/UI/Components/ModelDelete/ModelDelete";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import SideMenuComponent from "./SideMenu";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import ModelPage from "Common/UI/Components/Page/ModelPage";
|
||||
|
||||
const DeletePage: FunctionComponent = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<ModelPage<Project>
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
modelType={Project}
|
||||
title={"Project"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Projects",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECTS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Project",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECT_VIEW] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenuComponent modelId={modelId} />}
|
||||
>
|
||||
<ModelDelete
|
||||
modelType={Project}
|
||||
modelId={modelId}
|
||||
onDeleteSuccess={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.PROJECTS] as Route);
|
||||
}}
|
||||
/>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeletePage;
|
||||
@@ -1,90 +0,0 @@
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import Project from "Common/Models/DatabaseModels/Project";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import ModelPage from "Common/UI/Components/Page/ModelPage";
|
||||
import SideMenuComponent from "./SideMenu";
|
||||
|
||||
const Projects: FunctionComponent = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID();
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
modelType={Project}
|
||||
title={"Project"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Projects",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECTS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Project",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECT_VIEW] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenuComponent modelId={modelId} />}
|
||||
>
|
||||
<div>
|
||||
<CardModelDetail<Project>
|
||||
name="Project"
|
||||
cardProps={{
|
||||
title: "Project",
|
||||
description: "Project details",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Project"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: Project,
|
||||
id: "model-detail-user",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
_id: true,
|
||||
},
|
||||
title: "Project ID",
|
||||
fieldType: FieldType.Text,
|
||||
placeholder: "-",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
fieldType: FieldType.Text,
|
||||
},
|
||||
],
|
||||
modelId: modelId,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Projects;
|
||||
@@ -1,53 +0,0 @@
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import SideMenu from "Common/UI/Components/SideMenu/SideMenu";
|
||||
import SideMenuItem from "Common/UI/Components/SideMenu/SideMenuItem";
|
||||
import SideMenuSection from "Common/UI/Components/SideMenu/SideMenuSection";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
export interface SideMenuProps {
|
||||
modelId: ObjectID;
|
||||
}
|
||||
|
||||
const SideMenuComponent: FunctionComponent<SideMenuProps> = (
|
||||
props: SideMenuProps,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<SideMenu>
|
||||
<SideMenuSection title="Basic">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Overview",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECT_VIEW] as Route,
|
||||
{
|
||||
modelId: props.modelId,
|
||||
},
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Info}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Advanced">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Delete",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.PROJECT_DELETE] as Route,
|
||||
{
|
||||
modelId: props.modelId,
|
||||
},
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Trash}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
</SideMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideMenuComponent;
|
||||
@@ -3,11 +3,11 @@ import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import GlobalConfig from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "CommonUI/src/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "CommonUI/src/Components/Page/Page";
|
||||
import FieldType from "CommonUI/src/Components/Types/FieldType";
|
||||
import GlobalConfig from "Model/Models/GlobalConfig";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
|
||||
@@ -3,11 +3,11 @@ import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import GlobalConfig from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "CommonUI/src/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "CommonUI/src/Components/Page/Page";
|
||||
import FieldType from "CommonUI/src/Components/Types/FieldType";
|
||||
import GlobalConfig from "Model/Models/GlobalConfig";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
|
||||
@@ -3,11 +3,11 @@ import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import GlobalConfig from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "CommonUI/src/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "CommonUI/src/Components/Page/Page";
|
||||
import FieldType from "CommonUI/src/Components/Types/FieldType";
|
||||
import GlobalConfig from "Model/Models/GlobalConfig";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
@@ -72,9 +72,9 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioPrimaryPhoneNumber: true,
|
||||
twilioPhoneNumber: true,
|
||||
},
|
||||
title: "Primary Twilio Phone Number",
|
||||
title: "Twilio Phone Number",
|
||||
fieldType: FormFieldSchemaType.Phone,
|
||||
required: true,
|
||||
description: "You can find this in your Twilio console.",
|
||||
@@ -83,20 +83,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioSecondaryPhoneNumbers: true,
|
||||
},
|
||||
title: "Secondary Twilio Phone Number",
|
||||
fieldType: FormFieldSchemaType.LongText,
|
||||
required: true,
|
||||
description:
|
||||
"If you have bought more phone numbers from Twilio for specific countries, you can add them here.",
|
||||
placeholder: "+1234567890, +4444444444",
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
@@ -111,20 +97,12 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioPrimaryPhoneNumber: true,
|
||||
twilioPhoneNumber: true,
|
||||
},
|
||||
title: "Primary Twilio Phone Number",
|
||||
title: "Twilio Phone Number",
|
||||
fieldType: FieldType.Phone,
|
||||
placeholder: "None",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
twilioSecondaryPhoneNumbers: true,
|
||||
},
|
||||
title: "Secondary Twilio Phone Numbers",
|
||||
fieldType: FieldType.LongText,
|
||||
placeholder: "None",
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
|
||||
@@ -5,23 +5,21 @@ import Route from "Common/Types/API/Route";
|
||||
import { Green, Red } from "Common/Types/BrandColors";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import PageLoader from "Common/UI/Components/Loader/PageLoader";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import Pill from "Common/UI/Components/Pill/Pill";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import DropdownUtil from "Common/UI/Utils/Dropdown";
|
||||
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
|
||||
import GlobalConfig, {
|
||||
EmailServerType,
|
||||
} from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import ErrorMessage from "CommonUI/src/Components/ErrorMessage/ErrorMessage";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import PageLoader from "CommonUI/src/Components/Loader/PageLoader";
|
||||
import CardModelDetail from "CommonUI/src/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "CommonUI/src/Components/Page/Page";
|
||||
import Pill from "CommonUI/src/Components/Pill/Pill";
|
||||
import FieldType from "CommonUI/src/Components/Types/FieldType";
|
||||
import DropdownUtil from "CommonUI/src/Utils/Dropdown";
|
||||
import ModelAPI from "CommonUI/src/Utils/ModelAPI/ModelAPI";
|
||||
import GlobalConfig, { EmailServerType } from "Model/Models/GlobalConfig";
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
const [emailServerType, setemailServerType] = React.useState<EmailServerType>(
|
||||
EmailServerType.CustomSMTP,
|
||||
EmailServerType.Internal,
|
||||
);
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||
@@ -43,7 +41,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
|
||||
if (globalConfig) {
|
||||
setemailServerType(
|
||||
globalConfig.emailServerType || EmailServerType.CustomSMTP,
|
||||
globalConfig.emailServerType || EmailServerType.Internal,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,7 +59,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage message={error} />;
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -106,7 +104,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
title: "Admin Notification Email",
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: false,
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
@@ -127,7 +124,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
/>
|
||||
|
||||
<CardModelDetail
|
||||
name="Email Server Settings"
|
||||
name="Internal SMTP Settings"
|
||||
cardProps={{
|
||||
title: "Email Server Settings",
|
||||
description:
|
||||
@@ -172,7 +169,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
cardProps={{
|
||||
title: "Custom Email and SMTP Settings",
|
||||
description:
|
||||
"Please configure your SMTP server here to send emails.",
|
||||
"If you have not enabled Internal SMTP server to send emails. Please configure your SMTP server here.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit SMTP Config"
|
||||
@@ -200,7 +197,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
fieldType: FormFieldSchemaType.Hostname,
|
||||
required: true,
|
||||
placeholder: "smtp.server.com",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -231,7 +227,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: "emailuser",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -242,7 +237,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
fieldType: FormFieldSchemaType.EncryptedText,
|
||||
required: false,
|
||||
placeholder: "Password",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -253,9 +247,8 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
description:
|
||||
"Email used to log in to this SMTP Server. This is also the email your customers will see. ",
|
||||
"This is the display email your team and customers see, when they receive emails from OneUptime.",
|
||||
placeholder: "email@company.com",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -268,7 +261,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
description:
|
||||
"This is the display name your team and customers see, when they receive emails from OneUptime.",
|
||||
placeholder: "Company, Inc.",
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
|
||||
@@ -7,16 +7,16 @@ import IsNull from "Common/Types/BaseDatabase/IsNull";
|
||||
import { Green, Red } from "Common/Types/BrandColors";
|
||||
import OneUptimeDate from "Common/Types/Date";
|
||||
import { ErrorFunction, VoidFunction } from "Common/Types/FunctionTypes";
|
||||
import Banner from "Common/UI/Components/Banner/Banner";
|
||||
import { ButtonStyleType } from "Common/UI/Components/Button/Button";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ConfirmModal from "Common/UI/Components/Modal/ConfirmModal";
|
||||
import ModelTable from "Common/UI/Components/ModelTable/ModelTable";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import ProbeElement from "Common/UI/Components/Probe/Probe";
|
||||
import Statusbubble from "Common/UI/Components/StatusBubble/StatusBubble";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import Probe from "Common/Models/DatabaseModels/Probe";
|
||||
import Banner from "CommonUI/src/Components/Banner/Banner";
|
||||
import { ButtonStyleType } from "CommonUI/src/Components/Button/Button";
|
||||
import FormFieldSchemaType from "CommonUI/src/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ConfirmModal from "CommonUI/src/Components/Modal/ConfirmModal";
|
||||
import ModelTable from "CommonUI/src/Components/ModelTable/ModelTable";
|
||||
import Page from "CommonUI/src/Components/Page/Page";
|
||||
import ProbeElement from "CommonUI/src/Components/Probe/Probe";
|
||||
import Statusbubble from "CommonUI/src/Components/StatusBubble/StatusBubble";
|
||||
import FieldType from "CommonUI/src/Components/Types/FieldType";
|
||||
import Probe from "Model/Models/Probe";
|
||||
import React, { FunctionComponent, ReactElement, useState } from "react";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
@@ -54,11 +54,9 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
title="Need help with setting up Global Probes?"
|
||||
description="Here is a guide which will help you get set up"
|
||||
link={Route.fromString("/docs/probe/custom-probe")}
|
||||
hideOnMobile={true}
|
||||
/>
|
||||
|
||||
<ModelTable<Probe>
|
||||
userPreferencesKey={"admin-probes-table"}
|
||||
modelType={Probe}
|
||||
id="probes-table"
|
||||
name="Settings > Global Probes"
|
||||
@@ -101,7 +99,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: "Description",
|
||||
fieldType: FormFieldSchemaType.LongText,
|
||||
required: false,
|
||||
required: true,
|
||||
placeholder: "This probe is to monitor all the internal services.",
|
||||
},
|
||||
|
||||
@@ -153,7 +151,7 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
description: true,
|
||||
},
|
||||
title: "Description",
|
||||
type: FieldType.LongText,
|
||||
type: FieldType.Text,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
@@ -172,10 +170,8 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
noValueMessage: "-",
|
||||
title: "Description",
|
||||
type: FieldType.LongText,
|
||||
hideOnMobile: true,
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@@ -183,7 +179,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
},
|
||||
title: "Status",
|
||||
type: FieldType.Text,
|
||||
|
||||
getElement: (item: Probe): ReactElement => {
|
||||
if (
|
||||
item &&
|
||||
|
||||
@@ -2,9 +2,9 @@ import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../Utils/RouteMap";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import SideMenu from "Common/UI/Components/SideMenu/SideMenu";
|
||||
import SideMenuItem from "Common/UI/Components/SideMenu/SideMenuItem";
|
||||
import SideMenuSection from "Common/UI/Components/SideMenu/SideMenuSection";
|
||||
import SideMenu from "CommonUI/src/Components/SideMenu/SideMenu";
|
||||
import SideMenuItem from "CommonUI/src/Components/SideMenu/SideMenuItem";
|
||||
import SideMenuSection from "CommonUI/src/Components/SideMenu/SideMenuSection";
|
||||
import React, { ReactElement } from "react";
|
||||
|
||||
const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
|
||||
@@ -50,15 +50,6 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
|
||||
}}
|
||||
icon={IconProp.Call}
|
||||
/>
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "WhatsApp",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_WHATSAPP] as Route,
|
||||
),
|
||||
}}
|
||||
icon={IconProp.WhatsApp}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Monitoring">
|
||||
|
||||
@@ -1,491 +0,0 @@
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import GlobalConfig from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import React, { FunctionComponent, ReactElement, useState } from "react";
|
||||
import Card from "Common/UI/Components/Card/Card";
|
||||
import MarkdownViewer from "Common/UI/Components/Markdown.tsx/MarkdownViewer";
|
||||
import BasicForm from "Common/UI/Components/Forms/BasicForm";
|
||||
import Alert, { AlertType } from "Common/UI/Components/Alerts/Alert";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import API from "Common/UI/Utils/API/API";
|
||||
import { APP_API_URL } from "Common/UI/Config";
|
||||
import WhatsAppTemplateMessages, {
|
||||
WhatsAppTemplateId,
|
||||
WhatsAppTemplateIds,
|
||||
WhatsAppTemplateLanguage,
|
||||
} from "Common/Types/WhatsApp/WhatsAppTemplates";
|
||||
|
||||
type ToFriendlyName = (value: string) => string;
|
||||
|
||||
const toFriendlyName: ToFriendlyName = (value: string): string => {
|
||||
return value
|
||||
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
||||
.replace(/_/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
};
|
||||
|
||||
type ExtractTemplateVariables = (template: string) => Array<string>;
|
||||
|
||||
const extractTemplateVariables: ExtractTemplateVariables = (
|
||||
template: string,
|
||||
): Array<string> => {
|
||||
const matches: RegExpMatchArray | null = template.match(/\{\{(.*?)\}\}/g);
|
||||
|
||||
if (!matches) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const uniqueVariables: Set<string> = new Set<string>();
|
||||
|
||||
for (const match of matches) {
|
||||
const variable: string = match.replace("{{", "").replace("}}", "").trim();
|
||||
|
||||
if (variable) {
|
||||
uniqueVariables.add(variable);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(uniqueVariables).sort((a: string, b: string) => {
|
||||
return a.localeCompare(b);
|
||||
});
|
||||
};
|
||||
|
||||
type BuildWhatsAppSetupMarkdown = () => string;
|
||||
|
||||
const buildWhatsAppSetupMarkdown: BuildWhatsAppSetupMarkdown = (): string => {
|
||||
const templateKeys: Array<keyof typeof WhatsAppTemplateIds> = Object.keys(
|
||||
WhatsAppTemplateIds,
|
||||
) as Array<keyof typeof WhatsAppTemplateIds>;
|
||||
|
||||
const appApiBaseUrl: string = APP_API_URL.toString().replace(/\/$/, "");
|
||||
const primaryWebhookUrl: string = `${appApiBaseUrl}/notification/whatsapp/webhook`;
|
||||
|
||||
const description: string =
|
||||
"Follow these steps to connect Meta WhatsApp with OneUptime so notifications can be delivered via WhatsApp.";
|
||||
|
||||
const prerequisitesList: Array<string> = [
|
||||
"Meta Business Manager admin access for the WhatsApp Business Account.",
|
||||
"A WhatsApp Business phone number approved for API messaging.",
|
||||
"Admin access to OneUptime with permission to edit global notification settings.",
|
||||
"A webhook verify token string that you'll configure identically in Meta and OneUptime.",
|
||||
];
|
||||
|
||||
const setupStepsList: Array<string> = [
|
||||
"Sign in to the [Meta Business Manager](https://business.facebook.com/) with admin access to your WhatsApp Business Account.",
|
||||
"From **Business Settings → Accounts → WhatsApp Accounts**, create or select the account that owns your sender phone number.",
|
||||
"In Buisness Portfolio, create a system user and assign it to the WhatsApp Business Account with the role of **Admin**.",
|
||||
"Generate a token for this system user and this will be your long-lived access token. Make sure to select the **whatsapp_business_management** and **whatsapp_business_messaging** permissions when generating the token.",
|
||||
"Paste the access token, phone number ID, and webhook verify token into the **Meta WhatsApp Settings** card above, then save.",
|
||||
"For the **Business Account ID**, go to **Business Settings → Business Info** (or **Business Settings → WhatsApp Accounts → Settings**) and copy the **WhatsApp Business Account ID** value.",
|
||||
"To locate the **App ID** and **App Secret**, open [Meta for Developers](https://developers.facebook.com/apps/), select your WhatsApp app, then navigate to **Settings → Basic**. The App ID is shown at the top; click **Show** next to **App Secret** to reveal and copy it.",
|
||||
"Create each template listed below in the Meta WhatsApp Manager. Make sure the template name, language, and variables match exactly. You can however change the content to your preference. Please make sure it's approved by Meta.",
|
||||
"Send a test notification from OneUptime to confirm that WhatsApp delivery succeeds.",
|
||||
];
|
||||
|
||||
const prerequisitesMarkdown: string = prerequisitesList
|
||||
.map((item: string) => {
|
||||
return `- ${item}`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
const setupStepsMarkdown: string = setupStepsList
|
||||
.map((item: string, index: number) => {
|
||||
return `${index + 1}. ${item}`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
const tableRows: string = templateKeys
|
||||
.map((enumKey: keyof typeof WhatsAppTemplateIds) => {
|
||||
const templateId: WhatsAppTemplateId = WhatsAppTemplateIds[enumKey];
|
||||
const friendlyName: string = toFriendlyName(enumKey.toString());
|
||||
const templateMessage: string = WhatsAppTemplateMessages[templateId];
|
||||
const language: string = WhatsAppTemplateLanguage[templateId] || "en";
|
||||
const variables: Array<string> =
|
||||
extractTemplateVariables(templateMessage);
|
||||
const variableList: string =
|
||||
variables.length > 0
|
||||
? variables
|
||||
.map((variable: string) => {
|
||||
return `\`${variable}\``;
|
||||
})
|
||||
.join(", ")
|
||||
: "_None_";
|
||||
|
||||
return `| ${friendlyName} | \`${templateId}\` | ${language} | ${variableList} |`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
const templateBodies: string = templateKeys
|
||||
.map((enumKey: keyof typeof WhatsAppTemplateIds) => {
|
||||
const templateId: WhatsAppTemplateId = WhatsAppTemplateIds[enumKey];
|
||||
const friendlyName: string = toFriendlyName(enumKey.toString());
|
||||
const templateMessage: string = WhatsAppTemplateMessages[templateId];
|
||||
const language: string = WhatsAppTemplateLanguage[templateId] || "en";
|
||||
const variables: Array<string> =
|
||||
extractTemplateVariables(templateMessage);
|
||||
const variableMarkdown: string =
|
||||
variables.length > 0
|
||||
? variables
|
||||
.map((variable: string) => {
|
||||
return `- \`${variable}\``;
|
||||
})
|
||||
.join("\n")
|
||||
: "_None_";
|
||||
const variablesHeading: string = variables.length
|
||||
? `**Variables (${variables.length})**`
|
||||
: "**Variables**";
|
||||
|
||||
return [
|
||||
`#### ${friendlyName}`,
|
||||
"",
|
||||
`**Template Name:** \`${templateId}\``,
|
||||
`**Language:** ${language}`,
|
||||
"",
|
||||
variablesHeading,
|
||||
variableMarkdown,
|
||||
"",
|
||||
"**Body**",
|
||||
"```plaintext",
|
||||
templateMessage,
|
||||
"```",
|
||||
"",
|
||||
"---",
|
||||
].join("\n");
|
||||
})
|
||||
.join("\n\n");
|
||||
|
||||
const templateSummaryTable: string = [
|
||||
"| Friendly Name | Template Name | Language | Variables |",
|
||||
"| --- | --- | --- | --- |",
|
||||
tableRows,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
|
||||
const webhookSection: string = [
|
||||
"### Configure Meta Webhook Subscription",
|
||||
"1. In the OneUptime Admin Dashboard, open **Settings → WhatsApp → Meta WhatsApp Settings** and enter a strong value in **Webhook Verify Token**. Save the form so the encrypted token is stored in Global Config.",
|
||||
"2. Keep that verify token handy—Meta does not generate one for you. You'll paste the exact same value when configuring the callback.",
|
||||
"3. In [Meta for Developers](https://developers.facebook.com/apps/), select your WhatsApp app and navigate to **WhatsApp → Configuration → Webhooks**.",
|
||||
`4. Click **Configure**, then supply one of the following callback URLs when Meta asks for your endpoint:\n - \`${primaryWebhookUrl}\`\n `,
|
||||
"5. Paste the verify token from step 1 into Meta's **Verify Token** field and submit. Meta will call the callback URL and expect that value to match before it approves the subscription.",
|
||||
"6. After verification succeeds, subscribe to the **messages** field (and any other WhatsApp webhook categories you need) so delivery status updates are forwarded to OneUptime.",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n\n");
|
||||
|
||||
return [
|
||||
description,
|
||||
"### Prerequisites",
|
||||
prerequisitesMarkdown,
|
||||
"### Setup Steps",
|
||||
setupStepsMarkdown,
|
||||
webhookSection,
|
||||
"### Required WhatsApp Templates",
|
||||
templateSummaryTable,
|
||||
"### Template Bodies",
|
||||
"> Copy the exact template body below—including punctuation and spacing—when creating each template inside Meta. The variables list shows every placeholder that must be configured in WhatsApp Manager.",
|
||||
templateBodies,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n\n");
|
||||
};
|
||||
|
||||
const whatsappSetupMarkdown: string = buildWhatsAppSetupMarkdown();
|
||||
|
||||
const SettingsWhatsApp: FunctionComponent = (): ReactElement => {
|
||||
const [isSendingTest, setIsSendingTest] = useState<boolean>(false);
|
||||
const [testError, setTestError] = useState<string>("");
|
||||
const [testSuccess, setTestSuccess] = useState<string>("");
|
||||
|
||||
return (
|
||||
<Page
|
||||
title={"Admin Settings"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "WhatsApp",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_WHATSAPP] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
<CardModelDetail
|
||||
name="Meta WhatsApp Settings"
|
||||
cardProps={{
|
||||
title: "Meta WhatsApp Settings",
|
||||
description:
|
||||
"Configure Meta WhatsApp credentials. These values are used to send WhatsApp notifications from OneUptime.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Meta WhatsApp Config"
|
||||
formSteps={[
|
||||
{
|
||||
title: "Credentials",
|
||||
id: "meta-credentials",
|
||||
},
|
||||
{
|
||||
title: "Meta App",
|
||||
id: "meta-app",
|
||||
},
|
||||
]}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppAccessToken: true,
|
||||
},
|
||||
title: "Access Token",
|
||||
stepId: "meta-credentials",
|
||||
fieldType: FormFieldSchemaType.EncryptedText,
|
||||
required: true,
|
||||
description:
|
||||
"Long-lived access token generated in the Meta WhatsApp Business Platform.",
|
||||
placeholder: "EAAG...",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppPhoneNumberId: true,
|
||||
},
|
||||
title: "Phone Number ID",
|
||||
stepId: "meta-credentials",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description:
|
||||
"The WhatsApp Business phone number ID associated with your sending number.",
|
||||
placeholder: "123456789012345",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppBusinessAccountId: true,
|
||||
},
|
||||
title: "Business Account ID",
|
||||
stepId: "meta-credentials",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
description:
|
||||
"Optional Business Account ID that owns the WhatsApp templates.",
|
||||
placeholder: "123456789012345",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppWebhookVerifyToken: true,
|
||||
},
|
||||
title: "Webhook Verify Token",
|
||||
stepId: "meta-credentials",
|
||||
fieldType: FormFieldSchemaType.EncryptedText,
|
||||
required: false,
|
||||
description:
|
||||
"Secret token configured in Meta to validate webhook subscription requests.",
|
||||
placeholder: "Webhook verify token",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppAppId: true,
|
||||
},
|
||||
title: "App ID",
|
||||
stepId: "meta-app",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
description:
|
||||
"Optional Facebook App ID tied to your WhatsApp integration.",
|
||||
placeholder: "987654321098765",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppAppSecret: true,
|
||||
},
|
||||
title: "App Secret",
|
||||
stepId: "meta-app",
|
||||
fieldType: FormFieldSchemaType.EncryptedText,
|
||||
required: false,
|
||||
description:
|
||||
"Optional Facebook App Secret used for webhook signature verification.",
|
||||
placeholder: "Facebook App Secret",
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: "model-detail-global-config-meta-whatsapp",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppAccessToken: true,
|
||||
},
|
||||
title: "Access Token",
|
||||
fieldType: FieldType.HiddenText,
|
||||
placeholder: "Not Configured",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppPhoneNumberId: true,
|
||||
},
|
||||
title: "Phone Number ID",
|
||||
fieldType: FieldType.Text,
|
||||
placeholder: "Not Configured",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppBusinessAccountId: true,
|
||||
},
|
||||
title: "Business Account ID",
|
||||
fieldType: FieldType.Text,
|
||||
placeholder: "Not Configured",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppWebhookVerifyToken: true,
|
||||
},
|
||||
title: "Webhook Verify Token",
|
||||
fieldType: FieldType.HiddenText,
|
||||
placeholder: "Not Configured",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppAppId: true,
|
||||
},
|
||||
title: "App ID",
|
||||
fieldType: FieldType.Text,
|
||||
placeholder: "Not Configured",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppAppSecret: true,
|
||||
},
|
||||
title: "App Secret",
|
||||
fieldType: FieldType.HiddenText,
|
||||
placeholder: "Not Configured",
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
|
||||
<Card
|
||||
title="Send Test WhatsApp Message"
|
||||
description="Send a test WhatsApp template message to confirm your Meta configuration."
|
||||
>
|
||||
{testSuccess ? (
|
||||
<Alert
|
||||
type={AlertType.SUCCESS}
|
||||
title={testSuccess}
|
||||
className="mb-4"
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<BasicForm
|
||||
id="send-test-whatsapp-form"
|
||||
name="Send Test WhatsApp Message"
|
||||
isLoading={isSendingTest}
|
||||
error={testError || ""}
|
||||
submitButtonText="Send Test Message"
|
||||
maxPrimaryButtonWidth={true}
|
||||
initialValues={{
|
||||
phoneNumber: "",
|
||||
}}
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
phoneNumber: true,
|
||||
},
|
||||
title: "Recipient WhatsApp Number",
|
||||
description:
|
||||
"Enter the full international phone number (including country code) that should receive the test message.",
|
||||
placeholder: "+11234567890",
|
||||
required: true,
|
||||
fieldType: FormFieldSchemaType.Phone,
|
||||
disableSpellCheck: true,
|
||||
},
|
||||
]}
|
||||
onSubmit={async (
|
||||
values: JSONObject,
|
||||
onSubmitSuccessful?: () => void,
|
||||
) => {
|
||||
const toPhone: string = String(values["phoneNumber"] || "").trim();
|
||||
|
||||
if (!toPhone) {
|
||||
setTestSuccess("");
|
||||
setTestError(
|
||||
"Please enter a WhatsApp number before sending a test message.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSendingTest(true);
|
||||
setTestError("");
|
||||
setTestSuccess("");
|
||||
|
||||
try {
|
||||
const response: HTTPResponse<JSONObject> | HTTPErrorResponse =
|
||||
await API.post({
|
||||
url: URL.fromString(APP_API_URL.toString()).addRoute(
|
||||
"/notification/whatsapp/test",
|
||||
),
|
||||
data: {
|
||||
toPhone,
|
||||
templateKey: WhatsAppTemplateIds.TestNotification,
|
||||
templateLanguageCode:
|
||||
WhatsAppTemplateLanguage[
|
||||
WhatsAppTemplateIds.TestNotification
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
throw response;
|
||||
}
|
||||
|
||||
if (response.isFailure()) {
|
||||
throw new Error("Failed to send test WhatsApp message.");
|
||||
}
|
||||
|
||||
setTestSuccess(
|
||||
"Test WhatsApp message sent successfully. Check the recipient device to confirm delivery.",
|
||||
);
|
||||
|
||||
if (onSubmitSuccessful) {
|
||||
onSubmitSuccessful();
|
||||
}
|
||||
} catch (err) {
|
||||
setTestError(API.getFriendlyMessage(err));
|
||||
} finally {
|
||||
setIsSendingTest(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Meta WhatsApp Setup Guide"
|
||||
description="Steps to connect Meta WhatsApp and the templates you must provision."
|
||||
>
|
||||
<MarkdownViewer text={whatsappSetupMarkdown} />
|
||||
</Card>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsWhatsApp;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user