mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 08:42:13 +02:00
Compare commits
427 Commits
nativewind
...
refresh-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e0d8b487c | ||
|
|
c41b53dd2a | ||
|
|
5fe445330b | ||
|
|
38c744ce8c | ||
|
|
ad9771f222 | ||
|
|
20a3eab3a0 | ||
|
|
fbe198f0c0 | ||
|
|
b16743a669 | ||
|
|
bb48776e02 | ||
|
|
0f92342742 | ||
|
|
286c639857 | ||
|
|
6ed41b87dd | ||
|
|
12364415aa | ||
|
|
1a3301e715 | ||
|
|
8be7b68faf | ||
|
|
47f9d3914e | ||
|
|
8a1afbe7dc | ||
|
|
87ac6f3106 | ||
|
|
2df32e4cdd | ||
|
|
2e2adffe17 | ||
|
|
2a15cf8676 | ||
|
|
d249579c1c | ||
|
|
05681b108b | ||
|
|
36867a0b8c | ||
|
|
0e5a832628 | ||
|
|
46f69fdde5 | ||
|
|
70e6c0abe1 | ||
|
|
c5938956af | ||
|
|
e9bfe74b5d | ||
|
|
55085a5e6c | ||
|
|
9cb48a41e7 | ||
|
|
dd8179c0a7 | ||
|
|
f9f84d4104 | ||
|
|
827663675d | ||
|
|
0e8d7f2d6b | ||
|
|
0dd9dfb505 | ||
|
|
b3c68df77e | ||
|
|
06d0510667 | ||
|
|
059b7db474 | ||
|
|
ff17d990d1 | ||
|
|
2e6658542b | ||
|
|
04a4d6e4de | ||
|
|
2f595fe490 | ||
|
|
303554d644 | ||
|
|
2ca45b143d | ||
|
|
b5722256c5 | ||
|
|
0d231a6132 | ||
|
|
ecc1d841e2 | ||
|
|
cddf534449 | ||
|
|
a4ba662211 | ||
|
|
604bc33fb3 | ||
|
|
a1ae1bee89 | ||
|
|
775b8846c7 | ||
|
|
3837208023 | ||
|
|
b45910a22e | ||
|
|
c787d7eca0 | ||
|
|
9771c4cd69 | ||
|
|
4471d6bec4 | ||
|
|
f8a7330f79 | ||
|
|
ae177c920f | ||
|
|
9faa38454d | ||
|
|
a4198ec409 | ||
|
|
24d184debf | ||
|
|
f307c904b0 | ||
|
|
2d20b7fd13 | ||
|
|
fd738b23d0 | ||
|
|
deffa6489d | ||
|
|
e5f1d5553e | ||
|
|
d2ee3c5409 | ||
|
|
9a1ecd7fe0 | ||
|
|
b41d8ab5ab | ||
|
|
b809e1c43b | ||
|
|
743f8721f3 | ||
|
|
c0aa2b7905 | ||
|
|
4ac5819e6a | ||
|
|
158663c44b | ||
|
|
3d2bcfa579 | ||
|
|
21984c8684 | ||
|
|
ad63d18f0a | ||
|
|
e5af008079 | ||
|
|
3e72b2a9a4 | ||
|
|
6d66c6c369 | ||
|
|
9544dc2a6c | ||
|
|
a22e3b63e0 | ||
|
|
5f1f0cde4a | ||
|
|
3025880d6d | ||
|
|
00994b56c5 | ||
|
|
d56fd8bf69 | ||
|
|
1229d5d204 | ||
|
|
c4f1f4e711 | ||
|
|
7b8986b011 | ||
|
|
46e6176e6a | ||
|
|
61f9572956 | ||
|
|
3adc6901da | ||
|
|
d7f1bfb52a | ||
|
|
53968e681c | ||
|
|
3b8c854744 | ||
|
|
669ed2580c | ||
|
|
2f29c2e24c | ||
|
|
f0a2f454e2 | ||
|
|
d8206e12de | ||
|
|
f84434ada4 | ||
|
|
d5fbe0443e | ||
|
|
2732cd65ed | ||
|
|
7624523446 | ||
|
|
5851286548 | ||
|
|
123d9b07bc | ||
|
|
9edc6ac428 | ||
|
|
72fc633bf1 | ||
|
|
3264322054 | ||
|
|
d8fedc0b19 | ||
|
|
fc7cc5fe7f | ||
|
|
5b4eb72521 | ||
|
|
d84cfe9b09 | ||
|
|
0e8926a786 | ||
|
|
12ff3062de | ||
|
|
30aad2866f | ||
|
|
3de636ab9e | ||
|
|
a1bf9cbaae | ||
|
|
12c800b81f | ||
|
|
677e687662 | ||
|
|
93719d67be | ||
|
|
7d23209198 | ||
|
|
4461127a36 | ||
|
|
8326bf2c9e | ||
|
|
be9d2f6beb | ||
|
|
214dae6204 | ||
|
|
71c845d94e | ||
|
|
87d709dd05 | ||
|
|
25332f99fd | ||
|
|
1ac6e71f7e | ||
|
|
f1efd65ada | ||
|
|
bc338f41c7 | ||
|
|
39153735b5 | ||
|
|
ae9a78f1f4 | ||
|
|
f224ad6092 | ||
|
|
1abb8bc83f | ||
|
|
46704b7c5a | ||
|
|
143d91ceab | ||
|
|
ea58cacd1b | ||
|
|
5e19849ac8 | ||
|
|
f7c05645a9 | ||
|
|
1c1a48b78f | ||
|
|
13860be56d | ||
|
|
38c29664ea | ||
|
|
df1507b314 | ||
|
|
65c999b5fc | ||
|
|
803d0436ca | ||
|
|
b98e7f13a5 | ||
|
|
0785f11abe | ||
|
|
f0d9f7c594 | ||
|
|
dc9463f73d | ||
|
|
37c8e8b6b6 | ||
|
|
763dfaa1c9 | ||
|
|
c2e0d402d5 | ||
|
|
cdc1526fbf | ||
|
|
13ebd34e8f | ||
|
|
3b97c23039 | ||
|
|
7c15424565 | ||
|
|
6817443d9a | ||
|
|
f40a6395a6 | ||
|
|
81eb735aab | ||
|
|
923339710b | ||
|
|
1f9ec3011c | ||
|
|
668093b09c | ||
|
|
f39f51e8ee | ||
|
|
0bff616ca9 | ||
|
|
709b9b8343 | ||
|
|
aa93252407 | ||
|
|
b4e7ffce43 | ||
|
|
1053d22d3f | ||
|
|
a37bdb303d | ||
|
|
860af1bbf3 | ||
|
|
d5e72c1af2 | ||
|
|
6cc6fc4bf1 | ||
|
|
f890f24650 | ||
|
|
d077e55241 | ||
|
|
a4b7c99b8a | ||
|
|
671be425ae | ||
|
|
d2d7a51842 | ||
|
|
1142a20d64 | ||
|
|
8d0d7dc759 | ||
|
|
cedf06ba42 | ||
|
|
b347e18749 | ||
|
|
50e9a53547 | ||
|
|
d0de004498 | ||
|
|
65c4998048 | ||
|
|
5e7a3795c7 | ||
|
|
cf83319a90 | ||
|
|
87dc9d88d0 | ||
|
|
444cf040a6 | ||
|
|
2754657a6f | ||
|
|
38ca6b1e9e | ||
|
|
f481ef4f5e | ||
|
|
ad9adca473 | ||
|
|
819bd54a1f | ||
|
|
e212079b4a | ||
|
|
bb09dafbcc | ||
|
|
60c472cc09 | ||
|
|
0e272f0f31 | ||
|
|
f5de74611d | ||
|
|
03d157b850 | ||
|
|
da21cfc1ff | ||
|
|
2781bf0583 | ||
|
|
69b16c1c85 | ||
|
|
727f009d79 | ||
|
|
65d916f349 | ||
|
|
4373c7b49c | ||
|
|
34737fbba4 | ||
|
|
90fcfd1c7e | ||
|
|
b55320f02c | ||
|
|
34dc078197 | ||
|
|
2eacc90714 | ||
|
|
9d93d59f91 | ||
|
|
d84039e621 | ||
|
|
4eb46cf8a0 | ||
|
|
1ef27b7f52 | ||
|
|
412bd370df | ||
|
|
1131b80a52 | ||
|
|
8b55f5c348 | ||
|
|
159e5c4023 | ||
|
|
4970538d43 | ||
|
|
d7ca021d52 | ||
|
|
27eff7f415 | ||
|
|
50543ec7bf | ||
|
|
94c39408ed | ||
|
|
7219e1850f | ||
|
|
3180ed7149 | ||
|
|
79f32b80c8 | ||
|
|
cebfde6bf2 | ||
|
|
ca644d9dc7 | ||
|
|
ca4d9cb176 | ||
|
|
56204e02a9 | ||
|
|
c8e8a6d687 | ||
|
|
5bd2204eee | ||
|
|
72a31ed268 | ||
|
|
ee188dd050 | ||
|
|
3807aad63b | ||
|
|
055ec956fd | ||
|
|
e7767e59d1 | ||
|
|
f643e907b4 | ||
|
|
2eec57befd | ||
|
|
dd653f8deb | ||
|
|
f403c6a9e9 | ||
|
|
35f9b7f5c4 | ||
|
|
3c487ff9b9 | ||
|
|
41fca346b9 | ||
|
|
91b54ced67 | ||
|
|
ebdd97b8e9 | ||
|
|
8eb1eac629 | ||
|
|
a075b3c4dd | ||
|
|
738f901a51 | ||
|
|
683a8f5a58 | ||
|
|
160eba1ea4 | ||
|
|
89b65d1e02 | ||
|
|
caf709a38a | ||
|
|
d57433e4a0 | ||
|
|
5e2aa4e622 | ||
|
|
6cb51dd54b | ||
|
|
53ea843bdc | ||
|
|
71eeaf7ecd | ||
|
|
1d0168fcc6 | ||
|
|
99c3d440c5 | ||
|
|
5959ce728f | ||
|
|
0cb9e382a6 | ||
|
|
0c6d561b7c | ||
|
|
4291a76dd4 | ||
|
|
05b1f0ea82 | ||
|
|
b943505b1d | ||
|
|
1ed236eb91 | ||
|
|
44795182c9 | ||
|
|
50267534e0 | ||
|
|
f60836a4dc | ||
|
|
303294bb5e | ||
|
|
1212a8e4be | ||
|
|
25c626d2d4 | ||
|
|
ff6f9c89fa | ||
|
|
7f6e905c74 | ||
|
|
2cc64838aa | ||
|
|
3de5c8da8c | ||
|
|
7403ffa053 | ||
|
|
fa473474a2 | ||
|
|
20cbcf9a74 | ||
|
|
e6fd4b8304 | ||
|
|
4a3a743dcd | ||
|
|
6f1e2234d3 | ||
|
|
07189b4567 | ||
|
|
b1bc02cec4 | ||
|
|
6f7795aa31 | ||
|
|
50ee87c86f | ||
|
|
a75c6b6a43 | ||
|
|
f6168c969e | ||
|
|
2b0f9f2e7a | ||
|
|
14377c68fe | ||
|
|
9176fa2c9b | ||
|
|
4f29fef5f6 | ||
|
|
f28c7695ab | ||
|
|
bc234deb0e | ||
|
|
546b4a4fb3 | ||
|
|
1300c4e667 | ||
|
|
988d5d327c | ||
|
|
f2510a7b89 | ||
|
|
a2f16ca0eb | ||
|
|
1448288395 | ||
|
|
734481df6c | ||
|
|
43c534b76a | ||
|
|
1b593403b6 | ||
|
|
54e60cc380 | ||
|
|
28d9879dbd | ||
|
|
22c417ac92 | ||
|
|
401926c792 | ||
|
|
90bc4de84a | ||
|
|
93fd2a9ed7 | ||
|
|
231451e359 | ||
|
|
3073891a38 | ||
|
|
2b62d31e8b | ||
|
|
bc43e6e6ea | ||
|
|
2204ebde26 | ||
|
|
57a8547e14 | ||
|
|
53a70e1b93 | ||
|
|
3eb72ef7f9 | ||
|
|
2a471133c5 | ||
|
|
34e92679b2 | ||
|
|
8b0c2a7320 | ||
|
|
353d894394 | ||
|
|
09e4f0ff97 | ||
|
|
247c4bc637 | ||
|
|
72e2edd49d | ||
|
|
1be494169d | ||
|
|
3796053403 | ||
|
|
f316bfb9fd | ||
|
|
40f9613bd3 | ||
|
|
a3bb9f003f | ||
|
|
70714b2f21 | ||
|
|
a62543bff0 | ||
|
|
826a2006d0 | ||
|
|
a4075fe349 | ||
|
|
eb70a923f3 | ||
|
|
11d39898d3 | ||
|
|
8e5907d523 | ||
|
|
c9e57fcb19 | ||
|
|
8c6bc331a4 | ||
|
|
20129e606a | ||
|
|
55e0eede68 | ||
|
|
ffa603503d | ||
|
|
18429caabe | ||
|
|
ccbfef1cfe | ||
|
|
4789a15ce7 | ||
|
|
9633307de0 | ||
|
|
ee5c2a0f33 | ||
|
|
5c3b181507 | ||
|
|
6bbf2f866c | ||
|
|
60e4b51ec9 | ||
|
|
f244f872d4 | ||
|
|
3e910c1308 | ||
|
|
2b0067fd17 | ||
|
|
cb23b6b55b | ||
|
|
4406d52307 | ||
|
|
03b6bec6d0 | ||
|
|
ff0bd88b02 | ||
|
|
94290c77db | ||
|
|
003e44d331 | ||
|
|
b4cf798246 | ||
|
|
e607367809 | ||
|
|
82a7b5794c | ||
|
|
37c1674029 | ||
|
|
cd2a5222b8 | ||
|
|
875d6c5df9 | ||
|
|
7eea90a55a | ||
|
|
c401b86a2e | ||
|
|
e96f781157 | ||
|
|
73cc5f8f9e | ||
|
|
0ee9876de9 | ||
|
|
b76a75479d | ||
|
|
79b9cf4c06 | ||
|
|
a6da59c966 | ||
|
|
b84695feb9 | ||
|
|
b77973441d | ||
|
|
27e9c07c57 | ||
|
|
cbf8684d8c | ||
|
|
87057757a5 | ||
|
|
23b587f0f6 | ||
|
|
81c7a4eeb7 | ||
|
|
549dbfd6c7 | ||
|
|
a2eac673eb | ||
|
|
086a0a661d | ||
|
|
da0620eafa | ||
|
|
ca90ab0db4 | ||
|
|
94dacc20db | ||
|
|
7e887bd4cd | ||
|
|
b50dfcdf1c | ||
|
|
e2bc0ea4aa | ||
|
|
7d018b94d3 | ||
|
|
9fb7a70dc9 | ||
|
|
a37b3fc0b3 | ||
|
|
c06ef5ddfc | ||
|
|
0fb7174e94 | ||
|
|
84a7cd976d | ||
|
|
f7c8c00f04 | ||
|
|
51c3fcd3ca | ||
|
|
74c3dde7f1 | ||
|
|
6bf45f6f31 | ||
|
|
4de4ad8022 | ||
|
|
e263900115 | ||
|
|
34aaa34fb3 | ||
|
|
9f72a8e554 | ||
|
|
8dfabfd96f | ||
|
|
14cdc3ea86 | ||
|
|
cd6abe63ea | ||
|
|
aeb6d53b9d | ||
|
|
efa7224718 | ||
|
|
a2c406d7cc | ||
|
|
1933e37beb | ||
|
|
0b0336f9ea | ||
|
|
9304079a1c | ||
|
|
1e2dcf332b | ||
|
|
ca860f54a8 | ||
|
|
513e4146ed | ||
|
|
43d31ddbe9 | ||
|
|
18231f42aa | ||
|
|
60291cc218 | ||
|
|
036b29da51 | ||
|
|
d0d59147ae | ||
|
|
565fbe6cd3 | ||
|
|
12c3f9b25c | ||
|
|
9ee0e0f3cb | ||
|
|
50be2a666c |
49
.github/workflows/npm-audit-fix.yml
vendored
Normal file
49
.github/workflows/npm-audit-fix.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
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
|
||||
482
.github/workflows/release.yml
vendored
482
.github/workflows/release.yml
vendored
@@ -177,8 +177,21 @@ jobs:
|
||||
- name: Publish to npm
|
||||
run: |
|
||||
cd MCP
|
||||
npm publish --access public
|
||||
echo "✅ Published @oneuptime/mcp-server@${{ steps.version.outputs.version }} to npm"
|
||||
set +e
|
||||
PUBLISH_OUTPUT=$(npm publish --access public 2>&1)
|
||||
PUBLISH_EXIT=$?
|
||||
set -e
|
||||
echo "$PUBLISH_OUTPUT"
|
||||
if [ $PUBLISH_EXIT -ne 0 ]; then
|
||||
if echo "$PUBLISH_OUTPUT" | grep -q "You cannot publish over the previously published versions"; then
|
||||
echo "⚠️ npm publish skipped: version already published"
|
||||
else
|
||||
echo "❌ npm publish failed"
|
||||
exit $PUBLISH_EXIT
|
||||
fi
|
||||
else
|
||||
echo "✅ Published @oneuptime/mcp-server@${{ steps.version.outputs.version }} to npm"
|
||||
fi
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -205,13 +218,24 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--file ./MCP/Dockerfile.tpl \
|
||||
--tag oneuptime/mcp-server:${{ steps.version.outputs.version }} \
|
||||
--tag ghcr.io/oneuptime/mcp-server:${{ steps.version.outputs.version }} \
|
||||
--tag oneuptime/mcp-server:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/mcp-server:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{ steps.version.outputs.version }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
--push .
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--file ./MCP/Dockerfile.tpl \
|
||||
--tag oneuptime/mcp-server:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/mcp-server:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
--push .
|
||||
echo "✅ Pushed Docker images to Docker Hub and GitHub Container Registry"
|
||||
|
||||
@@ -282,14 +306,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Nginx/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/nginx:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/nginx:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/nginx:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/nginx:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Nginx/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/nginx:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/nginx:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
e2e-docker-image-deploy:
|
||||
@@ -352,14 +388,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./E2E/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/e2e:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/e2e:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/e2e:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/e2e:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./E2E/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/e2e:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/e2e:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
isolated-vm-docker-image-deploy:
|
||||
@@ -422,14 +470,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./IsolatedVM/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/isolated-vm:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/isolated-vm:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/isolated-vm:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/isolated-vm:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./IsolatedVM/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/isolated-vm:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/isolated-vm:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
home-docker-image-deploy:
|
||||
@@ -492,14 +552,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Home/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/home:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/home:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/home:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/home:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Home/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/home:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/home:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -565,14 +637,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./TestServer/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/test-server:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/test-server:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/test-server:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/test-server:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./TestServer/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/test-server:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/test-server:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
otel-collector-docker-image-deploy:
|
||||
@@ -635,14 +719,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./OTelCollector/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/otel-collector:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/otel-collector:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/otel-collector:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/otel-collector:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./OTelCollector/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/otel-collector:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/otel-collector:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -707,14 +803,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./StatusPage/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/status-page:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/status-page:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/status-page:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/status-page:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./StatusPage/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/status-page:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/status-page:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
test-docker-image-deploy:
|
||||
@@ -777,14 +885,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Tests/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/test:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/test:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/test:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/test:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Tests/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/test:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/test:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
probe-ingest-docker-image-deploy:
|
||||
@@ -847,14 +967,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./ProbeIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/probe-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/probe-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/probe-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/probe-ingest:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./ProbeIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/probe-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/probe-ingest:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -918,14 +1050,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./ServerMonitorIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/server-monitor-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/server-monitor-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/server-monitor-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/server-monitor-ingest:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./ServerMonitorIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/server-monitor-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/server-monitor-ingest:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -990,14 +1134,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./OpenTelemetryIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/open-telemetry-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/open-telemetry-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/open-telemetry-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/open-telemetry-ingest:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./OpenTelemetryIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/open-telemetry-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/open-telemetry-ingest:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1061,14 +1217,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./IncomingRequestIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/incoming-request-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/incoming-request-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/incoming-request-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/incoming-request-ingest:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./IncomingRequestIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/incoming-request-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/incoming-request-ingest:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
fluent-ingest-docker-image-deploy:
|
||||
@@ -1131,14 +1299,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./FluentIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/fluent-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/fluent-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/fluent-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/fluent-ingest:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./FluentIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/fluent-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/fluent-ingest:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
probe-docker-image-deploy:
|
||||
@@ -1201,14 +1381,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Probe/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/probe:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/probe:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/probe:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/probe:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Probe/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/probe:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/probe:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
admin-dashboard-docker-image-deploy:
|
||||
@@ -1271,14 +1463,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./AdminDashboard/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/admin-dashboard:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/admin-dashboard:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/admin-dashboard:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/admin-dashboard:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./AdminDashboard/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/admin-dashboard:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/admin-dashboard:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1342,14 +1546,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Dashboard/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/dashboard:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/dashboard:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/dashboard:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/dashboard:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Dashboard/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/dashboard:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/dashboard:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
app-docker-image-deploy:
|
||||
@@ -1412,14 +1628,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./App/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/app:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/app:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/app:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/app:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./App/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/app:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/app:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1483,14 +1711,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Copilot/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/copilot:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/copilot:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/copilot:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/copilot:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Copilot/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/copilot:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/copilot:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
accounts-docker-image-deploy:
|
||||
@@ -1553,14 +1793,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Accounts/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/accounts:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/accounts:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/accounts:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/accounts:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Accounts/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/accounts:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/accounts:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1667,14 +1919,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./LLM/Dockerfile \
|
||||
--platform linux/amd64 \
|
||||
--push \
|
||||
--tag oneuptime/llm:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/llm:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/llm:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/llm:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
./LLM
|
||||
docker buildx build \
|
||||
--file ./LLM/Dockerfile \
|
||||
--platform linux/amd64 \
|
||||
--push \
|
||||
--tag oneuptime/llm:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/llm:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
./LLM
|
||||
|
||||
docs-docker-image-deploy:
|
||||
@@ -1739,14 +2003,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Docs/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/docs:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/docs:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/docs:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/docs:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Docs/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/docs:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/docs:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1814,14 +2090,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Worker/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/worker:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/worker:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/worker:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/worker:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Worker/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/worker:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/worker:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1889,14 +2177,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./Workflow/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/workflow:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/workflow:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/workflow:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/workflow:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Workflow/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/workflow:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/workflow:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -2024,14 +2324,26 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx build \
|
||||
--file ./APIReference/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/api-reference:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/api-reference:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag oneuptime/api-reference:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/api-reference:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./APIReference/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/api-reference:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/api-reference:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
push-release-tags:
|
||||
@@ -2122,7 +2434,7 @@ jobs:
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx imagetools create \
|
||||
--tag oneuptime/${{ matrix.image }}:release \
|
||||
oneuptime/${{ matrix.image }}:${VERSION}
|
||||
ghcr.io/oneuptime/${{ matrix.image }}:${VERSION}
|
||||
|
||||
- name: Create GHCR release tag from version
|
||||
run: |
|
||||
@@ -2130,6 +2442,20 @@ jobs:
|
||||
docker buildx imagetools create \
|
||||
--tag ghcr.io/oneuptime/${{ matrix.image }}:release \
|
||||
ghcr.io/oneuptime/${{ matrix.image }}:${VERSION}
|
||||
|
||||
- name: Create Docker Hub enterprise release tag from version
|
||||
run: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx imagetools create \
|
||||
--tag oneuptime/${{ matrix.image }}:enterprise-release \
|
||||
ghcr.io/oneuptime/${{ matrix.image }}:enterprise-${VERSION}
|
||||
|
||||
- name: Create GHCR enterprise release tag from version
|
||||
run: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}"
|
||||
docker buildx imagetools create \
|
||||
--tag ghcr.io/oneuptime/${{ matrix.image }}:enterprise-release \
|
||||
ghcr.io/oneuptime/${{ matrix.image }}:enterprise-${VERSION}
|
||||
|
||||
|
||||
|
||||
|
||||
504
.github/workflows/test-release.yaml
vendored
504
.github/workflows/test-release.yaml
vendored
@@ -197,15 +197,28 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--file ./MCP/Dockerfile.tpl \
|
||||
--tag oneuptime/mcp-server:${{ steps.version.outputs.version }} \
|
||||
--tag oneuptime/mcp-server:${VERSION} \
|
||||
--tag oneuptime/mcp-server:test \
|
||||
--tag ghcr.io/oneuptime/mcp-server:${{ steps.version.outputs.version }} \
|
||||
--tag ghcr.io/oneuptime/mcp-server:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/mcp-server:test \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{ steps.version.outputs.version }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
--push .
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--file ./MCP/Dockerfile.tpl \
|
||||
--tag oneuptime/mcp-server:enterprise-${VERSION} \
|
||||
--tag oneuptime/mcp-server:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/mcp-server:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/mcp-server:enterprise-test \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
--push .
|
||||
echo "✅ Pushed test Docker images to Docker Hub and GitHub Container Registry"
|
||||
|
||||
@@ -301,16 +314,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./LLM/Dockerfile \
|
||||
--platform linux/amd64 \
|
||||
--push \
|
||||
--tag oneuptime/llm:test \
|
||||
--tag oneuptime/llm:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/llm:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/llm:test \
|
||||
--tag ghcr.io/oneuptime/llm:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/llm:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
./LLM
|
||||
docker buildx build \
|
||||
--file ./LLM/Dockerfile \
|
||||
--platform linux/amd64 \
|
||||
--push \
|
||||
--tag oneuptime/llm:enterprise-test \
|
||||
--tag oneuptime/llm:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/llm:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/llm:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
./LLM
|
||||
|
||||
|
||||
@@ -375,16 +402,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Nginx/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/nginx:test \
|
||||
--tag oneuptime/nginx:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/nginx:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/nginx:test \
|
||||
--tag ghcr.io/oneuptime/nginx:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/nginx:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Nginx/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/nginx:enterprise-test \
|
||||
--tag oneuptime/nginx:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/nginx:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/nginx:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -449,15 +490,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./E2E/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/e2e:test \
|
||||
--tag oneuptime/e2e:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/e2e:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/e2e:test \
|
||||
--tag ghcr.io/oneuptime/e2e:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/e2e:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./E2E/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/e2e:enterprise-test \
|
||||
--tag oneuptime/e2e:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/e2e:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/e2e:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
test-server-docker-image-deploy:
|
||||
@@ -521,16 +577,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./TestServer/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/test-server:test \
|
||||
--tag oneuptime/test-server:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/test-server:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/test-server:test \
|
||||
--tag ghcr.io/oneuptime/test-server:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/test-server:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./TestServer/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/test-server:enterprise-test \
|
||||
--tag oneuptime/test-server:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/test-server:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/test-server:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
otel-collector-docker-image-deploy:
|
||||
@@ -594,16 +664,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./OTelCollector/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/otel-collector:test \
|
||||
--tag oneuptime/otel-collector:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/otel-collector:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/otel-collector:test \
|
||||
--tag ghcr.io/oneuptime/otel-collector:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/otel-collector:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./OTelCollector/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/otel-collector:enterprise-test \
|
||||
--tag oneuptime/otel-collector:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/otel-collector:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/otel-collector:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
isolated-vm-docker-image-deploy:
|
||||
@@ -667,16 +751,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./IsolatedVM/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/isolated-vm:test \
|
||||
--tag oneuptime/isolated-vm:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/isolated-vm:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/isolated-vm:test \
|
||||
--tag ghcr.io/oneuptime/isolated-vm:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/isolated-vm:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./IsolatedVM/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/isolated-vm:enterprise-test \
|
||||
--tag oneuptime/isolated-vm:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/isolated-vm:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/isolated-vm:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
home-docker-image-deploy:
|
||||
@@ -740,16 +838,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Home/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/home:test \
|
||||
--tag oneuptime/home:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/home:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/home:test \
|
||||
--tag ghcr.io/oneuptime/home:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/home:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Home/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/home:enterprise-test \
|
||||
--tag oneuptime/home:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/home:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/home:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -815,16 +927,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./StatusPage/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/status-page:test \
|
||||
--tag oneuptime/status-page:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/status-page:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/status-page:test \
|
||||
--tag ghcr.io/oneuptime/status-page:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/status-page:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./StatusPage/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/status-page:enterprise-test \
|
||||
--tag oneuptime/status-page:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/status-page:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/status-page:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -890,16 +1016,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Tests/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/test:test \
|
||||
--tag oneuptime/test:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/test:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/test:test \
|
||||
--tag ghcr.io/oneuptime/test:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/test:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Tests/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/test:enterprise-test \
|
||||
--tag oneuptime/test:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/test:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/test:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
probe-ingest-docker-image-deploy:
|
||||
@@ -963,16 +1103,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./ProbeIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/probe-ingest:test \
|
||||
--tag oneuptime/probe-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/probe-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/probe-ingest:test \
|
||||
--tag ghcr.io/oneuptime/probe-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/probe-ingest:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./ProbeIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/probe-ingest:enterprise-test \
|
||||
--tag oneuptime/probe-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/probe-ingest:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/probe-ingest:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1038,16 +1192,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./ServerMonitorIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/server-monitor-ingest:test \
|
||||
--tag oneuptime/server-monitor-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/server-monitor-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/server-monitor-ingest:test \
|
||||
--tag ghcr.io/oneuptime/server-monitor-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/server-monitor-ingest:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./ServerMonitorIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/server-monitor-ingest:enterprise-test \
|
||||
--tag oneuptime/server-monitor-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/server-monitor-ingest:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/server-monitor-ingest:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1114,17 +1282,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./IncomingRequestIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/incoming-request-ingest:test \
|
||||
--tag oneuptime/incoming-request-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/incoming-request-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/incoming-request-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/incoming-request-ingest:test \
|
||||
--tag ghcr.io/oneuptime/incoming-request-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/incoming-request-ingest:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./IncomingRequestIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/incoming-request-ingest:enterprise-test \
|
||||
--tag oneuptime/incoming-request-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/incoming-request-ingest:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/incoming-request-ingest:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
open-telemetry-ingest-docker-image-deploy:
|
||||
@@ -1188,16 +1369,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./OpenTelemetryIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/open-telemetry-ingest:test \
|
||||
--tag oneuptime/open-telemetry-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/open-telemetry-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/open-telemetry-ingest:test \
|
||||
--tag ghcr.io/oneuptime/open-telemetry-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/open-telemetry-ingest:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./OpenTelemetryIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/open-telemetry-ingest:enterprise-test \
|
||||
--tag oneuptime/open-telemetry-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/open-telemetry-ingest:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/open-telemetry-ingest:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
fluent-ingest-docker-image-deploy:
|
||||
@@ -1261,16 +1456,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./FluentIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/fluent-ingest:test \
|
||||
--tag oneuptime/fluent-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/fluent-ingest:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/fluent-ingest:test \
|
||||
--tag ghcr.io/oneuptime/fluent-ingest:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/fluent-ingest:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./FluentIngest/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/fluent-ingest:enterprise-test \
|
||||
--tag oneuptime/fluent-ingest:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/fluent-ingest:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/fluent-ingest:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
probe-docker-image-deploy:
|
||||
@@ -1334,16 +1543,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Probe/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/probe:test \
|
||||
--tag oneuptime/probe:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/probe:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/probe:test \
|
||||
--tag ghcr.io/oneuptime/probe:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/probe:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Probe/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/probe:enterprise-test \
|
||||
--tag oneuptime/probe:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/probe:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/probe:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
dashboard-docker-image-deploy:
|
||||
@@ -1407,16 +1630,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Dashboard/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/dashboard:test \
|
||||
--tag oneuptime/dashboard:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/dashboard:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/dashboard:test \
|
||||
--tag ghcr.io/oneuptime/dashboard:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/dashboard:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Dashboard/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/dashboard:enterprise-test \
|
||||
--tag oneuptime/dashboard:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/dashboard:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/dashboard:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
admin-dashboard-docker-image-deploy:
|
||||
@@ -1480,16 +1717,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./AdminDashboard/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/admin-dashboard:test \
|
||||
--tag oneuptime/admin-dashboard:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/admin-dashboard:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/admin-dashboard:test \
|
||||
--tag ghcr.io/oneuptime/admin-dashboard:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/admin-dashboard:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./AdminDashboard/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/admin-dashboard:enterprise-test \
|
||||
--tag oneuptime/admin-dashboard:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/admin-dashboard:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/admin-dashboard:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
app-docker-image-deploy:
|
||||
@@ -1553,16 +1804,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./App/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/app:test \
|
||||
--tag oneuptime/app:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/app:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/app:test \
|
||||
--tag ghcr.io/oneuptime/app:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/app:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./App/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/app:enterprise-test \
|
||||
--tag oneuptime/app:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/app:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/app:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1629,17 +1894,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./APIReference/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/api-reference:test \
|
||||
--tag oneuptime/api-reference:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/api-reference:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/api-reference:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/api-reference:test \
|
||||
--tag ghcr.io/oneuptime/api-reference:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/api-reference:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./APIReference/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/api-reference:enterprise-test \
|
||||
--tag oneuptime/api-reference:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/api-reference:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/api-reference:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1705,17 +1983,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Accounts/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/accounts:test \
|
||||
--tag oneuptime/accounts:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/accounts:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/accounts:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/accounts:test \
|
||||
--tag ghcr.io/oneuptime/accounts:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/accounts:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Accounts/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/accounts:enterprise-test \
|
||||
--tag oneuptime/accounts:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/accounts:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/accounts:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
worker-docker-image-deploy:
|
||||
@@ -1779,16 +2070,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Worker/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/worker:${VERSION} \
|
||||
--tag oneuptime/worker:test \
|
||||
--tag oneuptime/worker:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/worker:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/worker:test \
|
||||
--tag ghcr.io/oneuptime/worker:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Worker/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/worker:enterprise-test \
|
||||
--tag oneuptime/worker:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/worker:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/worker:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
copilot-docker-image-deploy:
|
||||
@@ -1852,16 +2157,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Copilot/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/copilot:test \
|
||||
--tag oneuptime/copilot:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/copilot:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/copilot:test \
|
||||
--tag ghcr.io/oneuptime/copilot:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/copilot:${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Copilot/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/copilot:enterprise-test \
|
||||
--tag oneuptime/copilot:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/copilot:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/copilot:enterprise-${VERSION} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -1926,17 +2245,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Workflow/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/workflow:test \
|
||||
--tag oneuptime/workflow:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/workflow:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/workflow:test \
|
||||
--tag ghcr.io/oneuptime/workflow:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--tag ghcr.io/oneuptime/workflow:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Workflow/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/workflow:enterprise-test \
|
||||
--tag oneuptime/workflow:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/workflow:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/workflow:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
@@ -2001,16 +2333,30 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: |
|
||||
VERSION="${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test"
|
||||
docker buildx build \
|
||||
--file ./Docs/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/docs:test \
|
||||
--tag oneuptime/docs:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag oneuptime/docs:${VERSION} \
|
||||
--tag ghcr.io/oneuptime/docs:test \
|
||||
--tag ghcr.io/oneuptime/docs:${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}}-test \
|
||||
--tag ghcr.io/oneuptime/docs:${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${{needs.read-version.outputs.major_minor}}.${{needs.generate-build-number.outputs.build_number}} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=false \
|
||||
.
|
||||
docker buildx build \
|
||||
--file ./Docs/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--push \
|
||||
--tag oneuptime/docs:enterprise-test \
|
||||
--tag oneuptime/docs:enterprise-${VERSION} \
|
||||
--tag ghcr.io/oneuptime/docs:enterprise-test \
|
||||
--tag ghcr.io/oneuptime/docs:enterprise-${VERSION} \
|
||||
--build-arg GIT_SHA=${{ github.sha }} \
|
||||
--build-arg APP_VERSION=${VERSION} \
|
||||
--build-arg IS_ENTERPRISE_EDITION=true \
|
||||
.
|
||||
|
||||
|
||||
|
||||
@@ -14,9 +14,11 @@ 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
|
||||
|
||||
|
||||
|
||||
317
APIReference/package-lock.json
generated
317
APIReference/package-lock.json
generated
@@ -49,7 +49,9 @@
|
||||
"@opentelemetry/sdk-trace-web": "^1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.26.0",
|
||||
"@remixicon/react": "^4.2.0",
|
||||
"@simplewebauthn/server": "^13.2.2",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
@@ -58,7 +60,9 @@
|
||||
"@types/web-push": "^3.6.4",
|
||||
"acme-client": "^5.3.0",
|
||||
"airtable": "^0.12.2",
|
||||
"axios": "^1.7.2",
|
||||
"archiver": "^7.0.1",
|
||||
"axios": "^1.12.0",
|
||||
"botbuilder": "^4.23.3",
|
||||
"bullmq": "^5.3.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
@@ -80,10 +84,10 @@
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.9.10",
|
||||
"nodemailer": "^7.0.7",
|
||||
"otpauth": "^9.3.1",
|
||||
"pg": "^8.7.3",
|
||||
"playwright": "^1.50.0",
|
||||
"playwright": "^1.55.1",
|
||||
"posthog-js": "^1.139.6",
|
||||
"prop-types": "^15.8.1",
|
||||
"qrcode": "^1.5.3",
|
||||
@@ -99,7 +103,7 @@
|
||||
"react-router-dom": "^6.24.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.14.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-syntax-highlighter": "^16.0.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.11.4",
|
||||
"recharts": "^2.12.7",
|
||||
@@ -243,89 +247,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
|
||||
"integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.23.4",
|
||||
"chalk": "^2.4.2"
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz",
|
||||
@@ -501,19 +436,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
|
||||
"integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -528,109 +465,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.23.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz",
|
||||
"integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==",
|
||||
"version": "7.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
|
||||
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/traverse": "^7.23.6",
|
||||
"@babel/types": "^7.23.6"
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.28.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
|
||||
"integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.23.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz",
|
||||
"integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
|
||||
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.28.5"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
@@ -801,14 +657,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/parser": "^7.27.2",
|
||||
"@babel/types": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -836,14 +693,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.23.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz",
|
||||
"integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
|
||||
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.23.4",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.28.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1760,21 +1617,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -2004,10 +1863,11 @@
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
@@ -2076,9 +1936,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
||||
"integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==",
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"jake": "^10.8.5"
|
||||
},
|
||||
@@ -2225,9 +2086,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
@@ -2244,10 +2106,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -2542,6 +2405,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -3239,7 +3103,8 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
@@ -3398,12 +3263,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.2",
|
||||
"braces": "^3.0.3",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3684,10 +3550,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
@@ -4107,20 +3974,12 @@
|
||||
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="[&>:first-child]:mt-0 [&>:last-child]:mb-0">
|
||||
<%- include('../partials/code', {title: "Example Pagination Request", requestUrl: "/api/monitors/get-list?skip=0&limit=3", requestType: "GET", code: pageData.requestCode }) -%>
|
||||
<%- include('../partials/code', {title: "Example Pagination Request", requestUrl: "/api/monitors/get-list?skip=0&limit=3", requestType: "POST", code: pageData.requestCode }) -%>
|
||||
<%- include('../partials/code', {title: "Example Pagination Response" , requestUrl: "", requestType: "", code: pageData.responseCode }) -%>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,9 +14,11 @@ 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
|
||||
|
||||
|
||||
|
||||
88
Accounts/package-lock.json
generated
88
Accounts/package-lock.json
generated
@@ -12,7 +12,7 @@
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-router-dom": "^6.30.1",
|
||||
"use-async-effect": "^2.2.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -53,7 +53,9 @@
|
||||
"@opentelemetry/sdk-trace-web": "^1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.26.0",
|
||||
"@remixicon/react": "^4.2.0",
|
||||
"@simplewebauthn/server": "^13.2.2",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
@@ -62,7 +64,9 @@
|
||||
"@types/web-push": "^3.6.4",
|
||||
"acme-client": "^5.3.0",
|
||||
"airtable": "^0.12.2",
|
||||
"axios": "^1.7.2",
|
||||
"archiver": "^7.0.1",
|
||||
"axios": "^1.12.0",
|
||||
"botbuilder": "^4.23.3",
|
||||
"bullmq": "^5.3.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
@@ -84,10 +88,10 @@
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.9.10",
|
||||
"nodemailer": "^7.0.7",
|
||||
"otpauth": "^9.3.1",
|
||||
"pg": "^8.7.3",
|
||||
"playwright": "^1.50.0",
|
||||
"playwright": "^1.55.1",
|
||||
"posthog-js": "^1.139.6",
|
||||
"prop-types": "^15.8.1",
|
||||
"qrcode": "^1.5.3",
|
||||
@@ -103,7 +107,7 @@
|
||||
"react-router-dom": "^6.24.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.14.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-syntax-highlighter": "^16.0.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.11.4",
|
||||
"recharts": "^2.12.7",
|
||||
@@ -344,9 +348,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
|
||||
"integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==",
|
||||
"version": "1.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
|
||||
"integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -522,21 +526,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -683,9 +689,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
@@ -704,10 +710,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -794,6 +801,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -958,12 +966,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.23.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz",
|
||||
"integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==",
|
||||
"version": "6.30.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz",
|
||||
"integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.16.1"
|
||||
"@remix-run/router": "1.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -973,13 +981,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.23.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz",
|
||||
"integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==",
|
||||
"version": "6.30.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz",
|
||||
"integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.16.1",
|
||||
"react-router": "6.23.1"
|
||||
"@remix-run/router": "1.23.0",
|
||||
"react-router": "6.30.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -1009,6 +1017,16 @@
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
|
||||
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
|
||||
@@ -1021,15 +1039,6 @@
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier/node_modules/semver": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
|
||||
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
@@ -1047,6 +1056,7 @@
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-router-dom": "^6.30.1",
|
||||
"use-async-effect": "^2.2.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -12,6 +12,7 @@ import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchem
|
||||
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";
|
||||
@@ -192,6 +193,9 @@ 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">
|
||||
|
||||
@@ -14,9 +14,11 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,74 @@
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import Express, { ExpressApplication } from "Common/Server/Utils/Express";
|
||||
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";
|
||||
|
||||
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
|
||||
@@ -19,6 +80,7 @@ const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
liveCheck: async () => {},
|
||||
readyCheck: async () => {},
|
||||
},
|
||||
getVariablesToRenderIndexPage: ensureMasterAdminAccess,
|
||||
});
|
||||
|
||||
// add default routes
|
||||
|
||||
89
AdminDashboard/package-lock.json
generated
89
AdminDashboard/package-lock.json
generated
@@ -12,7 +12,7 @@
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1"
|
||||
"react-router-dom": "^6.30.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.35",
|
||||
@@ -52,7 +52,9 @@
|
||||
"@opentelemetry/sdk-trace-web": "^1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.26.0",
|
||||
"@remixicon/react": "^4.2.0",
|
||||
"@simplewebauthn/server": "^13.2.2",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
@@ -61,7 +63,9 @@
|
||||
"@types/web-push": "^3.6.4",
|
||||
"acme-client": "^5.3.0",
|
||||
"airtable": "^0.12.2",
|
||||
"axios": "^1.7.2",
|
||||
"archiver": "^7.0.1",
|
||||
"axios": "^1.12.0",
|
||||
"botbuilder": "^4.23.3",
|
||||
"bullmq": "^5.3.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
@@ -83,10 +87,10 @@
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.9.10",
|
||||
"nodemailer": "^7.0.7",
|
||||
"otpauth": "^9.3.1",
|
||||
"pg": "^8.7.3",
|
||||
"playwright": "^1.50.0",
|
||||
"playwright": "^1.55.1",
|
||||
"posthog-js": "^1.139.6",
|
||||
"prop-types": "^15.8.1",
|
||||
"qrcode": "^1.5.3",
|
||||
@@ -102,7 +106,7 @@
|
||||
"react-router-dom": "^6.24.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.14.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-syntax-highlighter": "^16.0.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.11.4",
|
||||
"recharts": "^2.12.7",
|
||||
@@ -343,9 +347,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
|
||||
"integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==",
|
||||
"version": "1.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
|
||||
"integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -491,21 +495,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -601,9 +607,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
@@ -620,10 +627,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -710,6 +718,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -937,12 +946,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.23.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz",
|
||||
"integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==",
|
||||
"version": "6.30.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz",
|
||||
"integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.16.1"
|
||||
"@remix-run/router": "1.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -952,13 +961,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.23.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz",
|
||||
"integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==",
|
||||
"version": "6.30.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz",
|
||||
"integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.16.1",
|
||||
"react-router": "6.23.1"
|
||||
"@remix-run/router": "1.23.0",
|
||||
"react-router": "6.30.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -988,6 +997,16 @@
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
|
||||
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
|
||||
@@ -1000,15 +1019,6 @@
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier/node_modules/semver": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
|
||||
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
@@ -1026,6 +1036,7 @@
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1"
|
||||
"react-router-dom": "^6.30.1"
|
||||
},
|
||||
"scripts": {
|
||||
"dev-build": "NODE_ENV=development node esbuild.config.js",
|
||||
|
||||
@@ -3,6 +3,7 @@ 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 React, { FunctionComponent, ReactElement } from "react";
|
||||
@@ -27,6 +28,7 @@ const DashboardHeader: FunctionComponent = (): ReactElement => {
|
||||
}
|
||||
rightComponents={
|
||||
<>
|
||||
<EditionLabel className="mr-3 hidden md:inline-flex" />
|
||||
<Button
|
||||
title="Exit Admin"
|
||||
buttonStyle={ButtonStyleType.NORMAL}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "./Utils/API";
|
||||
import App from "./App";
|
||||
import Telemetry from "Common/UI/Utils/Telemetry/Telemetry";
|
||||
import React from "react";
|
||||
|
||||
@@ -68,6 +68,9 @@ const buildWhatsAppSetupMarkdown: BuildWhatsAppSetupMarkdown = (): string => {
|
||||
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.";
|
||||
|
||||
@@ -75,6 +78,7 @@ const buildWhatsAppSetupMarkdown: BuildWhatsAppSetupMarkdown = (): 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> = [
|
||||
@@ -82,7 +86,7 @@ const buildWhatsAppSetupMarkdown: BuildWhatsAppSetupMarkdown = (): string => {
|
||||
"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 and phone number ID into the **Meta WhatsApp Settings** card above, then save.",
|
||||
"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.",
|
||||
@@ -169,12 +173,25 @@ const buildWhatsAppSetupMarkdown: BuildWhatsAppSetupMarkdown = (): string => {
|
||||
.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",
|
||||
@@ -271,6 +288,18 @@ const SettingsWhatsApp: FunctionComponent = (): ReactElement => {
|
||||
"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,
|
||||
@@ -324,6 +353,14 @@ const SettingsWhatsApp: FunctionComponent = (): ReactElement => {
|
||||
fieldType: FieldType.Text,
|
||||
placeholder: "Not Configured",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppWebhookVerifyToken: true,
|
||||
},
|
||||
title: "Webhook Verify Token",
|
||||
fieldType: FieldType.HiddenText,
|
||||
placeholder: "Not Configured",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
metaWhatsAppAppId: true,
|
||||
|
||||
42
AdminDashboard/src/Utils/API.ts
Normal file
42
AdminDashboard/src/Utils/API.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import BaseAPI from "Common/UI/Utils/API/API";
|
||||
import { IDENTITY_URL } from "Common/UI/Config";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import { Logger } from "Common/UI/Utils/Logger";
|
||||
|
||||
const registerAdminDashboardAuthRefresh = (): void => {
|
||||
const refreshSession = async (): Promise<boolean> => {
|
||||
try {
|
||||
const response = await BaseAPI.post<JSONObject>({
|
||||
url: URL.fromURL(IDENTITY_URL).addRoute("/refresh-session"),
|
||||
options: {
|
||||
skipAuthRefresh: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
Logger.warn(
|
||||
`Admin dashboard session refresh failed with status ${response.statusCode}.`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return response.isSuccess();
|
||||
} catch (err) {
|
||||
Logger.error("Admin dashboard session refresh request failed.");
|
||||
Logger.error(err as Error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
BaseAPI.setRefreshSessionHandler(refreshSession);
|
||||
|
||||
BaseAPI.setRefreshFailureHandler(() => {
|
||||
Logger.warn("Admin dashboard session refresh failed; falling back to logout.");
|
||||
});
|
||||
};
|
||||
|
||||
registerAdminDashboardAuthRefresh();
|
||||
|
||||
export default BaseAPI;
|
||||
@@ -14,9 +14,11 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -14,9 +14,11 @@ import TelemetryAPI from "Common/Server/API/TelemetryAPI";
|
||||
import ProbeAPI from "Common/Server/API/ProbeAPI";
|
||||
import ProjectAPI from "Common/Server/API/ProjectAPI";
|
||||
import ProjectSsoAPI from "Common/Server/API/ProjectSSO";
|
||||
import WhatsAppLogAPI from "./WhatsAppLogAPI";
|
||||
|
||||
// Import API
|
||||
import ResellerPlanAPI from "Common/Server/API/ResellerPlanAPI";
|
||||
import EnterpriseLicenseAPI from "Common/Server/API/EnterpriseLicenseAPI";
|
||||
import MonitorAPI from "Common/Server/API/MonitorAPI";
|
||||
import ShortLinkAPI from "Common/Server/API/ShortLinkAPI";
|
||||
import StatusPageAPI from "Common/Server/API/StatusPageAPI";
|
||||
@@ -97,6 +99,9 @@ import IncidentInternalNoteService, {
|
||||
import IncidentNoteTemplateService, {
|
||||
Service as IncidentNoteTemplateServiceType,
|
||||
} from "Common/Server/Services/IncidentNoteTemplateService";
|
||||
import IncidentPostmortemTemplateService, {
|
||||
Service as IncidentPostmortemTemplateServiceType,
|
||||
} from "Common/Server/Services/IncidentPostmortemTemplateService";
|
||||
import TableViewService, {
|
||||
Service as TableViewServiceType,
|
||||
} from "Common/Server/Services/TableViewService";
|
||||
@@ -140,9 +145,6 @@ import LogService, {
|
||||
LogService as LogServiceType,
|
||||
} from "Common/Server/Services/LogService";
|
||||
|
||||
import TelemetryAttributeService, {
|
||||
TelemetryAttributeService as TelemetryAttributeServiceType,
|
||||
} from "Common/Server/Services/TelemetryAttributeService";
|
||||
import CopilotActionTypePriorityService, {
|
||||
Service as CopilotActionTypePriorityServiceType,
|
||||
} from "Common/Server/Services/CopilotActionTypePriorityService";
|
||||
@@ -285,9 +287,6 @@ import ShortLinkService, {
|
||||
import SmsLogService, {
|
||||
Service as SmsLogServiceType,
|
||||
} from "Common/Server/Services/SmsLogService";
|
||||
import WhatsAppLogService, {
|
||||
Service as WhatsAppLogServiceType,
|
||||
} from "Common/Server/Services/WhatsAppLogService";
|
||||
import PushNotificationLogService, {
|
||||
Service as PushNotificationLogServiceType,
|
||||
} from "Common/Server/Services/PushNotificationLogService";
|
||||
@@ -382,6 +381,7 @@ import TelemetryExceptionService, {
|
||||
import ExceptionInstanceService, {
|
||||
ExceptionInstanceService as ExceptionInstanceServiceType,
|
||||
} from "Common/Server/Services/ExceptionInstanceService";
|
||||
import AcmeChallengeAPI from "Common/Server/API/AcmeChallengeAPI";
|
||||
|
||||
import FeatureSet from "Common/Server/Types/FeatureSet";
|
||||
import Express, { ExpressApplication } from "Common/Server/Utils/Express";
|
||||
@@ -412,6 +412,7 @@ import Incident from "Common/Models/DatabaseModels/Incident";
|
||||
import IncidentCustomField from "Common/Models/DatabaseModels/IncidentCustomField";
|
||||
import IncidentInternalNote from "Common/Models/DatabaseModels/IncidentInternalNote";
|
||||
import IncidentNoteTemplate from "Common/Models/DatabaseModels/IncidentNoteTemplate";
|
||||
import IncidentPostmortemTemplate from "Common/Models/DatabaseModels/IncidentPostmortemTemplate";
|
||||
import IncidentOwnerTeam from "Common/Models/DatabaseModels/IncidentOwnerTeam";
|
||||
import IncidentOwnerUser from "Common/Models/DatabaseModels/IncidentOwnerUser";
|
||||
import IncidentPublicNote from "Common/Models/DatabaseModels/IncidentPublicNote";
|
||||
@@ -462,7 +463,6 @@ import ServiceCatalogOwnerUser from "Common/Models/DatabaseModels/ServiceCatalog
|
||||
import ServiceCopilotCodeRepository from "Common/Models/DatabaseModels/ServiceCopilotCodeRepository";
|
||||
import ShortLink from "Common/Models/DatabaseModels/ShortLink";
|
||||
import SmsLog from "Common/Models/DatabaseModels/SmsLog";
|
||||
import WhatsAppLog from "Common/Models/DatabaseModels/WhatsAppLog";
|
||||
import StatusPageAnnouncement from "Common/Models/DatabaseModels/StatusPageAnnouncement";
|
||||
// Custom Fields API
|
||||
import StatusPageCustomField from "Common/Models/DatabaseModels/StatusPageCustomField";
|
||||
@@ -492,7 +492,6 @@ import WorkflowVariable from "Common/Models/DatabaseModels/WorkflowVariable";
|
||||
import ProbeOwnerTeam from "Common/Models/DatabaseModels/ProbeOwnerTeam";
|
||||
import ProbeOwnerUser from "Common/Models/DatabaseModels/ProbeOwnerUser";
|
||||
import ServiceCatalogDependency from "Common/Models/DatabaseModels/ServiceCatalogDependency";
|
||||
import TelemetryAttribute from "Common/Models/AnalyticsModels/TelemetryAttribute";
|
||||
import ExceptionInstance from "Common/Models/AnalyticsModels/ExceptionInstance";
|
||||
import TelemetyException from "Common/Models/DatabaseModels/TelemetryException";
|
||||
import CopilotActionTypePriority from "Common/Models/DatabaseModels/CopilotActionTypePriority";
|
||||
@@ -618,10 +617,7 @@ const BaseAPIFeatureSet: FeatureSet = {
|
||||
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
new BaseAnalyticsAPI<TelemetryAttribute, TelemetryAttributeServiceType>(
|
||||
TelemetryAttribute,
|
||||
TelemetryAttributeService,
|
||||
).getRouter(),
|
||||
new AcmeChallengeAPI().getRouter(),
|
||||
);
|
||||
|
||||
app.use(`/${APP_NAME.toLocaleLowerCase()}`, OpenAPI.getRouter());
|
||||
@@ -1403,6 +1399,17 @@ const BaseAPIFeatureSet: FeatureSet = {
|
||||
).getRouter(),
|
||||
);
|
||||
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
new BaseAPI<
|
||||
IncidentPostmortemTemplate,
|
||||
IncidentPostmortemTemplateServiceType
|
||||
>(
|
||||
IncidentPostmortemTemplate,
|
||||
IncidentPostmortemTemplateService,
|
||||
).getRouter(),
|
||||
);
|
||||
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
new BaseAPI<
|
||||
@@ -1545,10 +1552,7 @@ const BaseAPIFeatureSet: FeatureSet = {
|
||||
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
new BaseAPI<WhatsAppLog, WhatsAppLogServiceType>(
|
||||
WhatsAppLog,
|
||||
WhatsAppLogService,
|
||||
).getRouter(),
|
||||
new WhatsAppLogAPI().getRouter(),
|
||||
);
|
||||
|
||||
app.use(
|
||||
@@ -1648,6 +1652,10 @@ const BaseAPIFeatureSet: FeatureSet = {
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
new ResellerPlanAPI().getRouter(),
|
||||
);
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
new EnterpriseLicenseAPI().getRouter(),
|
||||
);
|
||||
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new SlackAPI().getRouter());
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
|
||||
14
App/FeatureSet/BaseAPI/WhatsAppLogAPI.ts
Normal file
14
App/FeatureSet/BaseAPI/WhatsAppLogAPI.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import BaseAPI from "Common/Server/API/BaseAPI";
|
||||
import WhatsAppLog from "Common/Models/DatabaseModels/WhatsAppLog";
|
||||
import WhatsAppLogService, {
|
||||
Service as WhatsAppLogServiceType,
|
||||
} from "Common/Server/Services/WhatsAppLogService";
|
||||
|
||||
export default class WhatsAppLogAPI extends BaseAPI<
|
||||
WhatsAppLog,
|
||||
WhatsAppLogServiceType
|
||||
> {
|
||||
public constructor() {
|
||||
super(WhatsAppLog, WhatsAppLogService);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,9 @@ import MailService from "Common/Server/Services/MailService";
|
||||
import UserService from "Common/Server/Services/UserService";
|
||||
import UserTotpAuthService from "Common/Server/Services/UserTotpAuthService";
|
||||
import CookieUtil from "Common/Server/Utils/Cookie";
|
||||
import JSONWebToken, {
|
||||
RefreshTokenData,
|
||||
} from "Common/Server/Utils/JsonWebToken";
|
||||
import Express, {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
@@ -40,6 +43,10 @@ import User from "Common/Models/DatabaseModels/User";
|
||||
import UserTotpAuth from "Common/Models/DatabaseModels/UserTotpAuth";
|
||||
import UserWebAuthn from "Common/Models/DatabaseModels/UserWebAuthn";
|
||||
import UserWebAuthnService from "Common/Server/Services/UserWebAuthnService";
|
||||
import HashedString from "Common/Types/HashedString";
|
||||
import NotAuthenticatedException from "Common/Types/Exception/NotAuthenticatedException";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import JSONWebTokenData from "Common/Types/JsonWebTokenData";
|
||||
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
@@ -186,12 +193,29 @@ router.post(
|
||||
// Refresh Permissions for this user here.
|
||||
await AccessTokenService.refreshUserAllPermissions(savedUser.id!);
|
||||
|
||||
CookieUtil.setUserCookie({
|
||||
const session = CookieUtil.setUserCookie({
|
||||
expressResponse: res,
|
||||
user: savedUser,
|
||||
isGlobalLogin: true,
|
||||
});
|
||||
|
||||
const hashedSessionId: string = await HashedString.hashValue(
|
||||
session.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
await UserService.updateOneBy({
|
||||
query: {
|
||||
_id: savedUser.id!,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: hashedSessionId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info("User signed up: " + savedUser.email?.toString());
|
||||
|
||||
return Response.sendEntityResponse(req, res, savedUser, User);
|
||||
@@ -495,6 +519,67 @@ router.post(
|
||||
next: NextFunction,
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const refreshToken: string | undefined =
|
||||
CookieUtil.getCookieFromExpressRequest(
|
||||
req,
|
||||
CookieUtil.getRefreshTokenKey(),
|
||||
);
|
||||
|
||||
let userIdToInvalidate: ObjectID | null = null;
|
||||
|
||||
if (refreshToken) {
|
||||
try {
|
||||
const refreshTokenData: RefreshTokenData =
|
||||
JSONWebToken.decodeRefreshToken(refreshToken);
|
||||
userIdToInvalidate = refreshTokenData.userId;
|
||||
} catch (err) {
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Failed to decode refresh token during logout: ${
|
||||
error.message || "unknown error"
|
||||
}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!userIdToInvalidate) {
|
||||
const accessToken: string | undefined =
|
||||
CookieUtil.getCookieFromExpressRequest(
|
||||
req,
|
||||
CookieUtil.getUserTokenKey(),
|
||||
);
|
||||
|
||||
if (accessToken) {
|
||||
try {
|
||||
const decoded: JSONWebTokenData = JSONWebToken.decode(accessToken);
|
||||
userIdToInvalidate = decoded.userId;
|
||||
} catch (err) {
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Failed to decode access token during logout: ${
|
||||
error.message || "unknown error"
|
||||
}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (userIdToInvalidate) {
|
||||
await UserService.updateOneBy({
|
||||
query: {
|
||||
_id: userIdToInvalidate,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: null!,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
CookieUtil.removeAllCookies(req, res);
|
||||
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
@@ -555,6 +640,122 @@ router.post(
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/refresh-session",
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction,
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const refreshToken: string | undefined =
|
||||
CookieUtil.getCookieFromExpressRequest(
|
||||
req,
|
||||
CookieUtil.getRefreshTokenKey(),
|
||||
);
|
||||
|
||||
if (!refreshToken) {
|
||||
CookieUtil.removeAllCookies(req, res);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthenticatedException("Refresh token missing"),
|
||||
);
|
||||
}
|
||||
|
||||
let refreshTokenData: RefreshTokenData;
|
||||
try {
|
||||
refreshTokenData = JSONWebToken.decodeRefreshToken(refreshToken);
|
||||
} catch (err) {
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Failed to decode refresh token: ${error.message || "unknown error"}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
CookieUtil.removeAllCookies(req, res);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthenticatedException("Refresh token is invalid"),
|
||||
);
|
||||
}
|
||||
|
||||
const hashedSessionId: string = await HashedString.hashValue(
|
||||
refreshTokenData.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
const user: User | null = await UserService.findOneBy({
|
||||
query: {
|
||||
_id: refreshTokenData.userId,
|
||||
jwtRefreshToken: hashedSessionId,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
isMasterAdmin: true,
|
||||
profilePictureId: true,
|
||||
timezone: true,
|
||||
enableTwoFactorAuth: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
CookieUtil.removeAllCookies(req, res);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthenticatedException("Refresh token does not match"),
|
||||
);
|
||||
}
|
||||
|
||||
const session = CookieUtil.setUserCookie({
|
||||
expressResponse: res,
|
||||
user: user,
|
||||
isGlobalLogin: refreshTokenData.isGlobalLogin,
|
||||
});
|
||||
|
||||
if (!req.cookies) {
|
||||
req.cookies = {} as Dictionary<string>;
|
||||
}
|
||||
|
||||
req.cookies[CookieUtil.getUserTokenKey()] = session.accessToken;
|
||||
req.cookies[CookieUtil.getRefreshTokenKey()] = session.refreshToken;
|
||||
|
||||
const hashedNewSessionId: string = await HashedString.hashValue(
|
||||
session.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
await UserService.updateOneBy({
|
||||
query: {
|
||||
_id: user.id!,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: hashedNewSessionId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`User session refreshed: ${
|
||||
user.email?.toString() || user.id?.toString() || "unknown"
|
||||
}`,
|
||||
);
|
||||
|
||||
return Response.sendEntityResponse(req, res, user, User);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
type FetchTotpAuthListFunction = (userId: ObjectID) => Promise<{
|
||||
totpAuthList: Array<UserTotpAuth>;
|
||||
webAuthnList: Array<UserWebAuthn>;
|
||||
@@ -788,12 +989,29 @@ const login: LoginFunction = async (options: {
|
||||
if (alreadySavedUser.password.toString() === user.password!.toString()) {
|
||||
logger.info("User logged in: " + alreadySavedUser.email?.toString());
|
||||
|
||||
CookieUtil.setUserCookie({
|
||||
const session = CookieUtil.setUserCookie({
|
||||
expressResponse: res,
|
||||
user: alreadySavedUser,
|
||||
isGlobalLogin: true,
|
||||
});
|
||||
|
||||
const hashedSessionId: string = await HashedString.hashValue(
|
||||
session.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
await UserService.updateOneBy({
|
||||
query: {
|
||||
_id: alreadySavedUser.id!,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: hashedSessionId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendEntityResponse(req, res, alreadySavedUser, User);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,41 +834,53 @@ router.post(
|
||||
projectId: projectId,
|
||||
name: displayName,
|
||||
},
|
||||
select: { _id: true },
|
||||
select: {
|
||||
_id: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
projectId: true,
|
||||
},
|
||||
props: { isRoot: true },
|
||||
});
|
||||
|
||||
let targetTeam: Team;
|
||||
let createdNewTeam: boolean = false;
|
||||
|
||||
if (existingTeam) {
|
||||
logger.debug(
|
||||
`SCIM Create group - team already exists with id: ${existingTeam.id}`,
|
||||
`SCIM Create group - team already exists with id: ${existingTeam.id}, reusing existing team`,
|
||||
);
|
||||
throw new BadRequestException("Group with this name already exists");
|
||||
targetTeam = existingTeam;
|
||||
} else {
|
||||
// Create new team
|
||||
logger.debug(`SCIM Create group - creating new team: ${displayName}`);
|
||||
const team: Team = new Team();
|
||||
team.projectId = projectId;
|
||||
team.name = displayName;
|
||||
team.isTeamEditable = true; // Allow editing SCIM-created teams
|
||||
team.isTeamDeleteable = true; // Allow deleting SCIM-created teams
|
||||
team.shouldHaveAtLeastOneMember = false; // SCIM groups can be empty
|
||||
|
||||
const createdTeam: Team = await TeamService.create({
|
||||
data: team,
|
||||
props: { isRoot: true },
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
`SCIM Create group - created team with id: ${createdTeam.id}`,
|
||||
);
|
||||
|
||||
targetTeam = createdTeam;
|
||||
createdNewTeam = true;
|
||||
}
|
||||
|
||||
// Create new team
|
||||
logger.debug(`SCIM Create group - creating new team: ${displayName}`);
|
||||
const team: Team = new Team();
|
||||
team.projectId = projectId;
|
||||
team.name = displayName;
|
||||
team.isTeamEditable = true; // Allow editing SCIM-created teams
|
||||
team.isTeamDeleteable = true; // Allow deleting SCIM-created teams
|
||||
team.shouldHaveAtLeastOneMember = false; // SCIM groups can be empty
|
||||
|
||||
const createdTeam: Team = await TeamService.create({
|
||||
data: team,
|
||||
props: { isRoot: true },
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
`SCIM Create group - created team with id: ${createdTeam.id}`,
|
||||
);
|
||||
|
||||
// Handle initial members if provided
|
||||
// Handle members if provided. Adds any new members and leaves existing ones intact.
|
||||
const members: Array<SCIMMember> =
|
||||
(scimGroup["members"] as Array<SCIMMember>) || [];
|
||||
if (members.length > 0) {
|
||||
logger.debug(
|
||||
`SCIM Create group - adding ${members.length} initial members`,
|
||||
`SCIM Create group - ensuring ${members.length} members are part of team ${targetTeam.id}`,
|
||||
);
|
||||
for (const member of members) {
|
||||
const userId: string = member["value"] as string;
|
||||
@@ -887,18 +899,18 @@ router.post(
|
||||
query: {
|
||||
projectId: projectId,
|
||||
userId: new ObjectID(userId),
|
||||
teamId: createdTeam.id!,
|
||||
teamId: targetTeam.id!,
|
||||
},
|
||||
select: { _id: true },
|
||||
props: { isRoot: true },
|
||||
});
|
||||
|
||||
if (!existingMember) {
|
||||
// Add user to the new team
|
||||
// Add user to the team
|
||||
const newTeamMember: TeamMember = new TeamMember();
|
||||
newTeamMember.projectId = projectId;
|
||||
newTeamMember.userId = new ObjectID(userId);
|
||||
newTeamMember.teamId = createdTeam.id!;
|
||||
newTeamMember.teamId = targetTeam.id!;
|
||||
newTeamMember.hasAcceptedInvitation = true;
|
||||
newTeamMember.invitationAcceptedAt =
|
||||
OneUptimeDate.getCurrentDate();
|
||||
@@ -910,7 +922,7 @@ router.post(
|
||||
},
|
||||
});
|
||||
logger.debug(
|
||||
`SCIM Create group - added user ${userId} to team`,
|
||||
`SCIM Create group - added user ${userId} to team ${targetTeam.id}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -918,18 +930,39 @@ router.post(
|
||||
}
|
||||
}
|
||||
|
||||
const createdGroup: JSONObject = await formatTeamForSCIM(
|
||||
createdTeam,
|
||||
const teamForResponse: Team | null = await TeamService.findOneById({
|
||||
id: targetTeam.id!,
|
||||
select: {
|
||||
_id: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
projectId: true,
|
||||
},
|
||||
props: { isRoot: true },
|
||||
});
|
||||
|
||||
if (!teamForResponse) {
|
||||
throw new NotFoundException("Failed to retrieve group");
|
||||
}
|
||||
|
||||
const groupResponse: JSONObject = await formatTeamForSCIM(
|
||||
teamForResponse,
|
||||
req.params["projectScimId"]!,
|
||||
true,
|
||||
);
|
||||
|
||||
logger.debug(
|
||||
`SCIM Create group - returning created group with id: ${createdTeam.id}`,
|
||||
`SCIM Create group - returning group with id: ${teamForResponse.id}`,
|
||||
);
|
||||
|
||||
res.status(201);
|
||||
return Response.sendJsonObjectResponse(req, res, createdGroup);
|
||||
if (createdNewTeam) {
|
||||
res.status(201);
|
||||
} else {
|
||||
res.status(200);
|
||||
}
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, groupResponse);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return next(err);
|
||||
@@ -995,10 +1028,6 @@ router.put(
|
||||
);
|
||||
}
|
||||
|
||||
if (!team.isTeamEditable) {
|
||||
throw new BadRequestException("This group cannot be updated");
|
||||
}
|
||||
|
||||
// Update team name if provided
|
||||
const displayName: string = scimGroup["displayName"] as string;
|
||||
if (displayName && displayName !== team.name) {
|
||||
@@ -1249,10 +1278,6 @@ router.patch(
|
||||
);
|
||||
}
|
||||
|
||||
if (!team.isTeamEditable) {
|
||||
throw new BadRequestException("This group cannot be updated");
|
||||
}
|
||||
|
||||
// Handle SCIM patch operations
|
||||
const operations: JSONObject[] =
|
||||
(scimPatch["Operations"] as JSONObject[]) || [];
|
||||
|
||||
@@ -15,7 +15,7 @@ import { JSONObject } from "Common/Types/JSON";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import PositiveNumber from "Common/Types/PositiveNumber";
|
||||
import DatabaseConfig from "Common/Server/DatabaseConfig";
|
||||
import { Host, HttpProtocol } from "Common/Server/EnvironmentConfig";
|
||||
import { EncryptionSecret, Host, HttpProtocol } from "Common/Server/EnvironmentConfig";
|
||||
import AccessTokenService from "Common/Server/Services/AccessTokenService";
|
||||
import ProjectSSOService from "Common/Server/Services/ProjectSsoService";
|
||||
import TeamMemberService from "Common/Server/Services/TeamMemberService";
|
||||
@@ -37,6 +37,7 @@ import TeamMember from "Common/Models/DatabaseModels/TeamMember";
|
||||
import User from "Common/Models/DatabaseModels/User";
|
||||
import xml2js from "xml2js";
|
||||
import Name from "Common/Types/Name";
|
||||
import HashedString from "Common/Types/HashedString";
|
||||
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
@@ -539,12 +540,29 @@ const loginUserWithSso: LoginUserWithSsoFunction = async (
|
||||
expressResponse: res,
|
||||
});
|
||||
|
||||
CookieUtil.setUserCookie({
|
||||
const session = CookieUtil.setUserCookie({
|
||||
expressResponse: res,
|
||||
user: alreadySavedUser,
|
||||
isGlobalLogin: false,
|
||||
});
|
||||
|
||||
const hashedSessionId: string = await HashedString.hashValue(
|
||||
session.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
await UserService.updateOneBy({
|
||||
query: {
|
||||
_id: alreadySavedUser.id!,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: hashedSessionId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Refresh Permissions for this user here.
|
||||
await AccessTokenService.refreshUserAllPermissions(alreadySavedUser.id!);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import URL from "Common/Types/API/URL";
|
||||
import OneUptimeDate from "Common/Types/Date";
|
||||
import EmailTemplateType from "Common/Types/Email/EmailTemplateType";
|
||||
import BadDataException from "Common/Types/Exception/BadDataException";
|
||||
import NotAuthenticatedException from "Common/Types/Exception/NotAuthenticatedException";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import JSONFunctions from "Common/Types/JSONFunctions";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
@@ -22,11 +23,16 @@ import Express, {
|
||||
ExpressRouter,
|
||||
NextFunction,
|
||||
} from "Common/Server/Utils/Express";
|
||||
import JSONWebToken from "Common/Server/Utils/JsonWebToken";
|
||||
import JSONWebToken, {
|
||||
RefreshTokenData,
|
||||
} from "Common/Server/Utils/JsonWebToken";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import Response from "Common/Server/Utils/Response";
|
||||
import StatusPage from "Common/Models/DatabaseModels/StatusPage";
|
||||
import StatusPagePrivateUser from "Common/Models/DatabaseModels/StatusPagePrivateUser";
|
||||
import HashedString from "Common/Types/HashedString";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import JSONWebTokenData from "Common/Types/JsonWebTokenData";
|
||||
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
@@ -46,7 +52,79 @@ router.post(
|
||||
req.params["statuspageid"].toString(),
|
||||
);
|
||||
|
||||
CookieUtil.removeCookie(res, CookieUtil.getUserTokenKey(statusPageId)); // remove the cookie.
|
||||
const refreshTokenKey: string = CookieUtil.getRefreshTokenKey(statusPageId);
|
||||
const accessTokenKey: string = CookieUtil.getUserTokenKey(statusPageId);
|
||||
|
||||
const refreshToken: string | undefined =
|
||||
CookieUtil.getCookieFromExpressRequest(req, refreshTokenKey);
|
||||
|
||||
let userIdToInvalidate: ObjectID | null = null;
|
||||
|
||||
if (refreshToken) {
|
||||
try {
|
||||
const refreshData: RefreshTokenData =
|
||||
JSONWebToken.decodeRefreshToken(refreshToken);
|
||||
|
||||
if (
|
||||
refreshData.statusPageId &&
|
||||
refreshData.statusPageId.toString() === statusPageId.toString()
|
||||
) {
|
||||
userIdToInvalidate = refreshData.userId;
|
||||
}
|
||||
} catch (err) {
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Failed to decode status page refresh token during logout: ${
|
||||
error.message || "unknown error"
|
||||
}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!userIdToInvalidate) {
|
||||
const accessToken: string | undefined =
|
||||
CookieUtil.getCookieFromExpressRequest(req, accessTokenKey);
|
||||
|
||||
if (accessToken) {
|
||||
try {
|
||||
const decoded: JSONWebTokenData = JSONWebToken.decode(accessToken);
|
||||
|
||||
if (
|
||||
decoded.statusPageId &&
|
||||
decoded.statusPageId.toString() === statusPageId.toString()
|
||||
) {
|
||||
userIdToInvalidate = decoded.userId;
|
||||
}
|
||||
} catch (err) {
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Failed to decode status page access token during logout: ${
|
||||
error.message || "unknown error"
|
||||
}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (userIdToInvalidate) {
|
||||
await StatusPagePrivateUserService.updateOneBy({
|
||||
query: {
|
||||
_id: userIdToInvalidate,
|
||||
statusPageId: statusPageId,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: null!,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
CookieUtil.removeCookie(res, accessTokenKey);
|
||||
CookieUtil.removeCookie(res, refreshTokenKey);
|
||||
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
@@ -383,23 +461,41 @@ router.post(
|
||||
});
|
||||
|
||||
if (alreadySavedUser) {
|
||||
const token: string = JSONWebToken.sign({
|
||||
data: alreadySavedUser,
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(
|
||||
new PositiveNumber(30),
|
||||
),
|
||||
const session = CookieUtil.setStatusPageUserCookie({
|
||||
expressResponse: res,
|
||||
user: alreadySavedUser,
|
||||
statusPageId: alreadySavedUser.statusPageId!,
|
||||
});
|
||||
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
CookieUtil.getUserTokenKey(alreadySavedUser.statusPageId!),
|
||||
token,
|
||||
{
|
||||
httpOnly: true,
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
},
|
||||
if (!req.cookies) {
|
||||
req.cookies = {} as Dictionary<string>;
|
||||
}
|
||||
|
||||
req.cookies[CookieUtil.getUserTokenKey(alreadySavedUser.statusPageId!)] =
|
||||
session.accessToken;
|
||||
req.cookies[
|
||||
CookieUtil.getRefreshTokenKey(alreadySavedUser.statusPageId!)
|
||||
] = session.refreshToken;
|
||||
|
||||
const hashedSessionId: string = await HashedString.hashValue(
|
||||
session.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
await StatusPagePrivateUserService.updateOneBy({
|
||||
query: {
|
||||
_id: alreadySavedUser.id!,
|
||||
statusPageId: alreadySavedUser.statusPageId!,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: hashedSessionId,
|
||||
lastActive: OneUptimeDate.getCurrentDate(),
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendEntityResponse(
|
||||
req,
|
||||
res,
|
||||
@@ -407,7 +503,11 @@ router.post(
|
||||
StatusPagePrivateUser,
|
||||
{
|
||||
miscData: {
|
||||
token: token,
|
||||
accessToken: session.accessToken,
|
||||
refreshToken: session.refreshToken,
|
||||
accessTokenExpiresInSeconds: session.accessTokenExpiresInSeconds,
|
||||
refreshTokenExpiresInSeconds:
|
||||
session.refreshTokenExpiresInSeconds,
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -421,4 +521,160 @@ router.post(
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/refresh-session/:statuspageid",
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction,
|
||||
): Promise<void> => {
|
||||
try {
|
||||
if (!req.params["statuspageid"]) {
|
||||
throw new BadDataException("Status Page ID is required.");
|
||||
}
|
||||
|
||||
const statusPageId: ObjectID = new ObjectID(
|
||||
req.params["statuspageid"].toString(),
|
||||
);
|
||||
|
||||
const refreshTokenKey: string = CookieUtil.getRefreshTokenKey(statusPageId);
|
||||
const accessTokenKey: string = CookieUtil.getUserTokenKey(statusPageId);
|
||||
|
||||
const refreshToken: string | undefined =
|
||||
CookieUtil.getCookieFromExpressRequest(req, refreshTokenKey);
|
||||
|
||||
if (!refreshToken) {
|
||||
CookieUtil.removeCookie(res, refreshTokenKey);
|
||||
CookieUtil.removeCookie(res, accessTokenKey);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthenticatedException("Refresh token missing."),
|
||||
);
|
||||
}
|
||||
|
||||
let refreshTokenData: RefreshTokenData;
|
||||
|
||||
try {
|
||||
refreshTokenData = JSONWebToken.decodeRefreshToken(refreshToken);
|
||||
} catch (err) {
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Failed to decode status page refresh token: ${
|
||||
error.message || "unknown error"
|
||||
}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
CookieUtil.removeCookie(res, refreshTokenKey);
|
||||
CookieUtil.removeCookie(res, accessTokenKey);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthenticatedException("Refresh token is invalid."),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!refreshTokenData.statusPageId ||
|
||||
refreshTokenData.statusPageId.toString() !== statusPageId.toString()
|
||||
) {
|
||||
CookieUtil.removeCookie(res, refreshTokenKey);
|
||||
CookieUtil.removeCookie(res, accessTokenKey);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthenticatedException("Refresh token status page mismatch."),
|
||||
);
|
||||
}
|
||||
|
||||
const hashedSessionId: string = await HashedString.hashValue(
|
||||
refreshTokenData.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
const user: StatusPagePrivateUser | null =
|
||||
await StatusPagePrivateUserService.findOneBy({
|
||||
query: {
|
||||
_id: refreshTokenData.userId,
|
||||
statusPageId: statusPageId,
|
||||
jwtRefreshToken: hashedSessionId,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
email: true,
|
||||
statusPageId: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
CookieUtil.removeCookie(res, refreshTokenKey);
|
||||
CookieUtil.removeCookie(res, accessTokenKey);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthenticatedException("Refresh token does not match."),
|
||||
);
|
||||
}
|
||||
|
||||
const session = CookieUtil.setStatusPageUserCookie({
|
||||
expressResponse: res,
|
||||
user: user,
|
||||
statusPageId: statusPageId,
|
||||
});
|
||||
|
||||
if (!req.cookies) {
|
||||
req.cookies = {} as Dictionary<string>;
|
||||
}
|
||||
|
||||
req.cookies[accessTokenKey] = session.accessToken;
|
||||
req.cookies[refreshTokenKey] = session.refreshToken;
|
||||
|
||||
const hashedNewSessionId: string = await HashedString.hashValue(
|
||||
session.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
await StatusPagePrivateUserService.updateOneBy({
|
||||
query: {
|
||||
_id: user.id!,
|
||||
statusPageId: statusPageId,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: hashedNewSessionId,
|
||||
lastActive: OneUptimeDate.getCurrentDate(),
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`Status page session refreshed: ${
|
||||
user.email?.toString() || user.id?.toString() || "unknown"
|
||||
} for status page ${statusPageId.toString()}`,
|
||||
);
|
||||
|
||||
return Response.sendEntityResponse(
|
||||
req,
|
||||
res,
|
||||
user,
|
||||
StatusPagePrivateUser,
|
||||
{
|
||||
miscData: {
|
||||
accessToken: session.accessToken,
|
||||
refreshToken: session.refreshToken,
|
||||
accessTokenExpiresInSeconds: session.accessTokenExpiresInSeconds,
|
||||
refreshTokenExpiresInSeconds: session.refreshTokenExpiresInSeconds,
|
||||
},
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import WhatsAppService from "../Services/WhatsAppService";
|
||||
import BadDataException from "Common/Types/Exception/BadDataException";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import GlobalConfig from "Common/Models/DatabaseModels/GlobalConfig";
|
||||
import { JSONArray, JSONObject } from "Common/Types/JSON";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import Phone from "Common/Types/Phone";
|
||||
import WhatsAppMessage from "Common/Types/WhatsApp/WhatsAppMessage";
|
||||
@@ -9,17 +10,214 @@ import {
|
||||
WhatsAppTemplateIds,
|
||||
WhatsAppTemplateLanguage,
|
||||
} from "Common/Types/WhatsApp/WhatsAppTemplates";
|
||||
import WhatsAppStatus from "Common/Types/WhatsAppStatus";
|
||||
import ClusterKeyAuthorization from "Common/Server/Middleware/ClusterKeyAuthorization";
|
||||
import WhatsAppLogService from "Common/Server/Services/WhatsAppLogService";
|
||||
import GlobalConfigService from "Common/Server/Services/GlobalConfigService";
|
||||
import Express, {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
ExpressRouter,
|
||||
NextFunction,
|
||||
} from "Common/Server/Utils/Express";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import Response from "Common/Server/Utils/Response";
|
||||
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
const MAX_STATUS_MESSAGE_LENGTH: number = 500;
|
||||
|
||||
export const mapWebhookStatusToWhatsAppStatus: (
|
||||
status?: string,
|
||||
) => WhatsAppStatus = (status?: string): WhatsAppStatus => {
|
||||
switch ((status || "").toLowerCase()) {
|
||||
case "sent":
|
||||
return WhatsAppStatus.Sent;
|
||||
case "delivered":
|
||||
return WhatsAppStatus.Delivered;
|
||||
case "read":
|
||||
return WhatsAppStatus.Read;
|
||||
case "failed":
|
||||
return WhatsAppStatus.Failed;
|
||||
case "deleted":
|
||||
case "removed":
|
||||
return WhatsAppStatus.Deleted;
|
||||
case "warning":
|
||||
return WhatsAppStatus.Warning;
|
||||
case "queued":
|
||||
case "pending":
|
||||
return WhatsAppStatus.Queued;
|
||||
case "error":
|
||||
return WhatsAppStatus.Error;
|
||||
case "success":
|
||||
return WhatsAppStatus.Success;
|
||||
default:
|
||||
return WhatsAppStatus.Unknown;
|
||||
}
|
||||
};
|
||||
|
||||
export const buildStatusMessage: (payload: JSONObject) => string | undefined = (
|
||||
payload: JSONObject,
|
||||
): string | undefined => {
|
||||
const messageParts: Array<string> = [];
|
||||
const rawStatus: string | undefined = payload["status"]
|
||||
? String(payload["status"])
|
||||
: undefined;
|
||||
|
||||
if (rawStatus) {
|
||||
messageParts.push(`Status: ${rawStatus}`);
|
||||
}
|
||||
|
||||
const timestamp: string | undefined = payload["timestamp"]
|
||||
? String(payload["timestamp"])
|
||||
: undefined;
|
||||
|
||||
if (timestamp) {
|
||||
const numericTimestamp: number = Number(timestamp);
|
||||
if (!isNaN(numericTimestamp)) {
|
||||
messageParts.push(
|
||||
`Timestamp: ${new Date(numericTimestamp * 1000).toISOString()}`,
|
||||
);
|
||||
} else {
|
||||
messageParts.push(`Timestamp: ${timestamp}`);
|
||||
}
|
||||
}
|
||||
|
||||
const conversation: JSONObject | undefined =
|
||||
(payload["conversation"] as JSONObject | undefined) || undefined;
|
||||
|
||||
if (conversation) {
|
||||
if (conversation["id"]) {
|
||||
messageParts.push(`Conversation: ${conversation["id"]}`);
|
||||
}
|
||||
|
||||
const origin: JSONObject | undefined =
|
||||
(conversation["origin"] as JSONObject | undefined) || undefined;
|
||||
|
||||
if (origin?.["type"]) {
|
||||
messageParts.push(`Origin: ${origin["type"]}`);
|
||||
}
|
||||
|
||||
if (conversation["expiration_timestamp"]) {
|
||||
const expirationTimestamp: number = Number(
|
||||
conversation["expiration_timestamp"],
|
||||
);
|
||||
|
||||
if (!isNaN(expirationTimestamp)) {
|
||||
messageParts.push(
|
||||
`Conversation expires: ${new Date(expirationTimestamp * 1000).toISOString()}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pricing: JSONObject | undefined =
|
||||
(payload["pricing"] as JSONObject | undefined) || undefined;
|
||||
|
||||
if (pricing) {
|
||||
const pricingParts: Array<string> = [];
|
||||
|
||||
if (pricing["billable"] !== undefined) {
|
||||
pricingParts.push(`billable=${pricing["billable"]}`);
|
||||
}
|
||||
|
||||
if (pricing["category"]) {
|
||||
pricingParts.push(`category=${pricing["category"]}`);
|
||||
}
|
||||
|
||||
if (pricing["pricing_model"]) {
|
||||
pricingParts.push(`model=${pricing["pricing_model"]}`);
|
||||
}
|
||||
|
||||
if (pricingParts.length > 0) {
|
||||
messageParts.push(`Pricing: ${pricingParts.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
const errors: JSONArray | undefined =
|
||||
(payload["errors"] as JSONArray | undefined) || undefined;
|
||||
|
||||
if (Array.isArray(errors) && errors.length > 0) {
|
||||
const firstError: JSONObject = errors[0] as JSONObject;
|
||||
const errorParts: Array<string> = [];
|
||||
|
||||
if (firstError["title"]) {
|
||||
errorParts.push(String(firstError["title"]));
|
||||
}
|
||||
|
||||
if (firstError["code"]) {
|
||||
errorParts.push(`code=${firstError["code"]}`);
|
||||
}
|
||||
|
||||
if (firstError["detail"]) {
|
||||
errorParts.push(String(firstError["detail"]));
|
||||
}
|
||||
|
||||
if (errorParts.length > 0) {
|
||||
messageParts.push(`Error: ${errorParts.join(" - ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (messageParts.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const statusMessage: string = messageParts.join(" | ");
|
||||
|
||||
if (statusMessage.length <= MAX_STATUS_MESSAGE_LENGTH) {
|
||||
return statusMessage;
|
||||
}
|
||||
|
||||
return `${statusMessage.substring(0, MAX_STATUS_MESSAGE_LENGTH - 3)}...`;
|
||||
};
|
||||
|
||||
const updateWhatsAppLogStatus: (
|
||||
statusPayload: JSONObject,
|
||||
) => Promise<void> = async (statusPayload: JSONObject): Promise<void> => {
|
||||
const messageId: string | undefined = statusPayload["id"]
|
||||
? String(statusPayload["id"])
|
||||
: undefined;
|
||||
|
||||
if (!messageId) {
|
||||
logger.warn(
|
||||
`[Meta WhatsApp Webhook] Received status payload without message id. Payload: ${JSON.stringify(statusPayload)}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const rawStatus: string | undefined = statusPayload["status"]
|
||||
? String(statusPayload["status"])
|
||||
: undefined;
|
||||
|
||||
const derivedStatus: WhatsAppStatus =
|
||||
mapWebhookStatusToWhatsAppStatus(rawStatus);
|
||||
|
||||
const statusMessage: string | undefined = buildStatusMessage(statusPayload);
|
||||
|
||||
const updateResult: number = await WhatsAppLogService.updateOneBy({
|
||||
query: {
|
||||
whatsAppMessageId: messageId,
|
||||
},
|
||||
data: {
|
||||
status: derivedStatus,
|
||||
...(statusMessage ? { statusMessage } : {}),
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (updateResult === 0) {
|
||||
logger.warn(
|
||||
`[Meta WhatsApp Webhook] No WhatsApp log found for message id ${messageId}. Payload: ${JSON.stringify(statusPayload)}`,
|
||||
);
|
||||
} else {
|
||||
logger.debug(
|
||||
`[Meta WhatsApp Webhook] Updated WhatsApp log for message id ${messageId} with status ${derivedStatus}.`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const toTemplateVariables: (
|
||||
rawVariables: JSONObject | undefined,
|
||||
) => Record<string, string> | undefined = (
|
||||
@@ -119,6 +317,128 @@ router.post(
|
||||
},
|
||||
);
|
||||
|
||||
router.get("/webhook", async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
const mode: string | undefined = req.query["hub.mode"]
|
||||
? String(req.query["hub.mode"])
|
||||
: undefined;
|
||||
const verifyToken: string | undefined = req.query["hub.verify_token"]
|
||||
? String(req.query["hub.verify_token"])
|
||||
: undefined;
|
||||
const challenge: string | undefined = req.query["hub.challenge"]
|
||||
? String(req.query["hub.challenge"])
|
||||
: undefined;
|
||||
|
||||
if (mode === "subscribe" && challenge) {
|
||||
const globalConfigTokenResponse: GlobalConfig | null =
|
||||
await GlobalConfigService.findOneBy({
|
||||
query: {
|
||||
_id: ObjectID.getZeroObjectID().toString(),
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
metaWhatsAppWebhookVerifyToken: true,
|
||||
},
|
||||
});
|
||||
|
||||
const configuredVerifyToken: string | undefined =
|
||||
globalConfigTokenResponse?.metaWhatsAppWebhookVerifyToken?.trim() ||
|
||||
undefined;
|
||||
|
||||
if (!configuredVerifyToken) {
|
||||
logger.error(
|
||||
"Meta WhatsApp webhook verify token is not configured. Rejecting verification request.",
|
||||
);
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
if (verifyToken === configuredVerifyToken) {
|
||||
res.status(200).send(challenge);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warn(
|
||||
"Meta WhatsApp webhook verification failed due to token mismatch.",
|
||||
);
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
res.sendStatus(400);
|
||||
});
|
||||
|
||||
router.post(
|
||||
"/webhook",
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const body: JSONObject = req.body as JSONObject;
|
||||
|
||||
if (
|
||||
(body["object"] as string | undefined) !== "whatsapp_business_account"
|
||||
) {
|
||||
logger.debug(
|
||||
`[Meta WhatsApp Webhook] Received event for unsupported object: ${JSON.stringify(body)}`,
|
||||
);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
}
|
||||
|
||||
const entries: JSONArray | undefined = body["entry"] as
|
||||
| JSONArray
|
||||
| undefined;
|
||||
|
||||
if (!Array.isArray(entries)) {
|
||||
logger.warn(
|
||||
`[Meta WhatsApp Webhook] Payload did not include entries array. Payload: ${JSON.stringify(body)}`,
|
||||
);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
}
|
||||
|
||||
const statusUpdatePromises: Array<Promise<void>> = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
const entryObject: JSONObject = entry as JSONObject;
|
||||
const changes: JSONArray | undefined =
|
||||
(entryObject["changes"] as JSONArray | undefined) || undefined;
|
||||
|
||||
if (!Array.isArray(changes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const change of changes) {
|
||||
const changeObject: JSONObject = change as JSONObject;
|
||||
const value: JSONObject | undefined =
|
||||
(changeObject["value"] as JSONObject | undefined) || undefined;
|
||||
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const statuses: JSONArray | undefined =
|
||||
(value["statuses"] as JSONArray | undefined) || undefined;
|
||||
|
||||
if (Array.isArray(statuses)) {
|
||||
for (const statusItem of statuses) {
|
||||
statusUpdatePromises.push(
|
||||
updateWhatsAppLogStatus(statusItem as JSONObject),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (statusUpdatePromises.length > 0) {
|
||||
await Promise.allSettled(statusUpdatePromises);
|
||||
}
|
||||
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/test",
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
|
||||
@@ -68,8 +68,6 @@ export default class WhatsAppService {
|
||||
);
|
||||
}
|
||||
|
||||
const config: MetaWhatsAppConfig = await getMetaWhatsAppConfig();
|
||||
|
||||
const isSensitiveMessage: boolean = Boolean(options.isSensitive);
|
||||
const messageSummary: string = isSensitiveMessage
|
||||
? SENSITIVE_MESSAGE_PLACEHOLDER
|
||||
@@ -131,6 +129,8 @@ export default class WhatsAppService {
|
||||
whatsAppLog.onCallDutyPolicyScheduleId = options.onCallScheduleId;
|
||||
}
|
||||
|
||||
const config: MetaWhatsAppConfig = await getMetaWhatsAppConfig();
|
||||
|
||||
let messageCost: number = 0;
|
||||
const shouldChargeForMessage: boolean = IsBillingEnabled;
|
||||
|
||||
@@ -417,7 +417,11 @@ export default class WhatsAppService {
|
||||
}
|
||||
}
|
||||
|
||||
whatsAppLog.status = WhatsAppStatus.Success;
|
||||
if (messageId) {
|
||||
whatsAppLog.whatsAppMessageId = messageId;
|
||||
}
|
||||
|
||||
whatsAppLog.status = WhatsAppStatus.Sent;
|
||||
whatsAppLog.statusMessage = messageId
|
||||
? `Message ID: ${messageId}`
|
||||
: "WhatsApp message sent successfully";
|
||||
@@ -470,10 +474,14 @@ export default class WhatsAppService {
|
||||
await UserOnCallLogTimelineService.updateOneById({
|
||||
id: options.userOnCallLogTimelineId,
|
||||
data: {
|
||||
status:
|
||||
whatsAppLog.status === WhatsAppStatus.Success
|
||||
? UserNotificationStatus.Sent
|
||||
: UserNotificationStatus.Error,
|
||||
status: [
|
||||
WhatsAppStatus.Success,
|
||||
WhatsAppStatus.Sent,
|
||||
WhatsAppStatus.Delivered,
|
||||
WhatsAppStatus.Read,
|
||||
].includes(whatsAppLog.status || WhatsAppStatus.Error)
|
||||
? UserNotificationStatus.Sent
|
||||
: UserNotificationStatus.Error,
|
||||
statusMessage: whatsAppLog.statusMessage,
|
||||
},
|
||||
props: {
|
||||
|
||||
520
App/package-lock.json
generated
520
App/package-lock.json
generated
@@ -59,7 +59,9 @@
|
||||
"@opentelemetry/sdk-trace-web": "^1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.26.0",
|
||||
"@remixicon/react": "^4.2.0",
|
||||
"@simplewebauthn/server": "^13.2.2",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
@@ -68,7 +70,9 @@
|
||||
"@types/web-push": "^3.6.4",
|
||||
"acme-client": "^5.3.0",
|
||||
"airtable": "^0.12.2",
|
||||
"axios": "^1.7.2",
|
||||
"archiver": "^7.0.1",
|
||||
"axios": "^1.12.0",
|
||||
"botbuilder": "^4.23.3",
|
||||
"bullmq": "^5.3.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
@@ -90,10 +94,10 @@
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.9.10",
|
||||
"nodemailer": "^7.0.7",
|
||||
"otpauth": "^9.3.1",
|
||||
"pg": "^8.7.3",
|
||||
"playwright": "^1.50.0",
|
||||
"playwright": "^1.55.1",
|
||||
"posthog-js": "^1.139.6",
|
||||
"prop-types": "^15.8.1",
|
||||
"qrcode": "^1.5.3",
|
||||
@@ -109,7 +113,7 @@
|
||||
"react-router-dom": "^6.24.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.14.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-syntax-highlighter": "^16.0.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.11.4",
|
||||
"recharts": "^2.12.7",
|
||||
@@ -253,89 +257,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
|
||||
"integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.23.4",
|
||||
"chalk": "^2.4.2"
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz",
|
||||
@@ -511,19 +446,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
|
||||
"integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -538,109 +475,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.23.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz",
|
||||
"integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==",
|
||||
"version": "7.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
|
||||
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/traverse": "^7.23.6",
|
||||
"@babel/types": "^7.23.6"
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.28.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
|
||||
"integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.23.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz",
|
||||
"integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
|
||||
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.28.5"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
@@ -811,14 +667,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/parser": "^7.27.2",
|
||||
"@babel/types": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -846,14 +703,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.23.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz",
|
||||
"integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
|
||||
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.23.4",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.28.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1745,15 +1602,17 @@
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.1.tgz",
|
||||
"integrity": "sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
@@ -1863,21 +1722,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -1948,6 +1809,19 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -2108,6 +1982,7 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
@@ -2136,10 +2011,11 @@
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
@@ -2201,6 +2077,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -2231,6 +2108,20 @@
|
||||
"node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
@@ -2240,9 +2131,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
||||
"integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==",
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"jake": "^10.8.5"
|
||||
},
|
||||
@@ -2286,6 +2178,51 @@
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
@@ -2389,9 +2326,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
@@ -2408,10 +2346,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -2433,15 +2372,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
@@ -2452,12 +2392,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2511,14 +2454,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
|
||||
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3",
|
||||
"hasown": "^2.0.0"
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -2533,6 +2486,19 @@
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
@@ -2587,11 +2553,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.3"
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -2642,10 +2609,11 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -2653,10 +2621,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -2665,9 +2637,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
|
||||
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
@@ -2826,6 +2799,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -3523,7 +3497,8 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
@@ -3780,6 +3755,15 @@
|
||||
"tmpl": "1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
@@ -3787,12 +3771,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.2",
|
||||
"braces": "^3.0.3",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3803,6 +3788,7 @@
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -3811,6 +3797,7 @@
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
@@ -3875,9 +3862,10 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "6.9.7",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.7.tgz",
|
||||
"integrity": "sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==",
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz",
|
||||
"integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==",
|
||||
"license": "MIT-0",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
@@ -4120,10 +4108,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
@@ -4627,20 +4616,12 @@
|
||||
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
@@ -4910,9 +4891,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/xml-crypto": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.0.tgz",
|
||||
"integrity": "sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.1.tgz",
|
||||
"integrity": "sha512-0GUNbPtQt+PLMsC5HoZRONX+K6NBJEqpXe/lsvrFj0EqfpGPpVfJKGE7a5jCg8s2+Wkrf/2U1G41kIH+zC9eyQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.8.8",
|
||||
"xpath": "0.0.32"
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
"dependencies": {
|
||||
"@sendgrid/mail": "^8.1.0",
|
||||
"Common": "file:../Common",
|
||||
|
||||
|
||||
"ejs": "^3.1.9",
|
||||
"handlebars": "^4.7.8",
|
||||
"nodemailer": "^6.9.7",
|
||||
|
||||
@@ -3,6 +3,8 @@ import Route from "../../../Types/API/Route";
|
||||
import AnalyticsTableEngine from "../../../Types/AnalyticsDatabase/AnalyticsTableEngine";
|
||||
import AnalyticsTableColumn from "../../../Types/AnalyticsDatabase/TableColumn";
|
||||
import TableColumnType from "../../../Types/AnalyticsDatabase/TableColumnType";
|
||||
import Projection from "../../../Types/AnalyticsDatabase/Projection";
|
||||
import MaterializedView from "../../../Types/AnalyticsDatabase/MaterializedView";
|
||||
import {
|
||||
ColumnAccessControl,
|
||||
TableAccessControl,
|
||||
@@ -40,6 +42,8 @@ export default class AnalyticsBaseModel extends CommonModel {
|
||||
enableWorkflowOn?: EnableWorkflowOn | undefined;
|
||||
enableRealtimeEventsOn?: EnableRealtimeEventsOn | undefined;
|
||||
partitionKey: string;
|
||||
projections?: Array<Projection> | undefined;
|
||||
materializedViews?: Array<MaterializedView> | undefined;
|
||||
}) {
|
||||
super({
|
||||
tableColumns: data.tableColumns,
|
||||
@@ -140,6 +144,8 @@ export default class AnalyticsBaseModel extends CommonModel {
|
||||
this.crudApiPath = data.crudApiPath;
|
||||
this.enableRealtimeEventsOn = data.enableRealtimeEventsOn;
|
||||
this.partitionKey = data.partitionKey;
|
||||
this.projections = data.projections || [];
|
||||
this.materializedViews = data.materializedViews || [];
|
||||
}
|
||||
|
||||
private _enableWorkflowOn: EnableWorkflowOn | undefined;
|
||||
@@ -250,6 +256,22 @@ export default class AnalyticsBaseModel extends CommonModel {
|
||||
this._crudApiPath = v;
|
||||
}
|
||||
|
||||
private _projections: Array<Projection> = [];
|
||||
public get projections(): Array<Projection> {
|
||||
return this._projections;
|
||||
}
|
||||
public set projections(v: Array<Projection>) {
|
||||
this._projections = v;
|
||||
}
|
||||
|
||||
private _materializedViews: Array<MaterializedView> = [];
|
||||
public get materializedViews(): Array<MaterializedView> {
|
||||
return this._materializedViews;
|
||||
}
|
||||
public set materializedViews(v: Array<MaterializedView>) {
|
||||
this._materializedViews = v;
|
||||
}
|
||||
|
||||
public getTenantColumn(): AnalyticsTableColumn | null {
|
||||
const column: AnalyticsTableColumn | undefined = this.tableColumns.find(
|
||||
(column: AnalyticsTableColumn) => {
|
||||
|
||||
@@ -9,6 +9,332 @@ import { SpanStatus } from "./Span";
|
||||
|
||||
export default class ExceptionInstance extends AnalyticsBaseModel {
|
||||
public constructor() {
|
||||
const projectIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const serviceIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "serviceId",
|
||||
title: "Service ID",
|
||||
description: "ID of the Service which created the log",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const timeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "time",
|
||||
title: "Time",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const timeUnixNanoColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "timeUnixNano",
|
||||
title: "Time (in Unix Nano)",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const exceptionTypeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "exceptionType",
|
||||
title: "Exception Type",
|
||||
description: "Exception Type", // Examples: java.net.ConnectException; OSError; etc.
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const stackTraceColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "stackTrace",
|
||||
title: "Stack Trace",
|
||||
description: "Exception Stack Trace", // Examples: Division by zero; Can't convert 'int' object to str implicitly
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const messageColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "message",
|
||||
title: "Exception Message",
|
||||
description: "Exception Message", // Examples: Division by zero; Can't convert 'int' object to str implicitly
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const spanStatusCodeColumn: AnalyticsTableColumn = new AnalyticsTableColumn(
|
||||
{
|
||||
key: "spanStatusCode",
|
||||
title: "Span Status Code",
|
||||
description: "Span Status Code",
|
||||
required: false,
|
||||
type: TableColumnType.Number,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const escapedColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "escaped",
|
||||
title: "Exception Escaped",
|
||||
description: "Exception Escaped", // SHOULD be set to true if the exception event is recorded at a point where it is known that the exception is escaping the scope of the span.
|
||||
required: false,
|
||||
type: TableColumnType.Boolean,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const traceIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "traceId",
|
||||
title: "Trace ID",
|
||||
description: "ID of the trace",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const spanIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "spanId",
|
||||
title: "Span ID",
|
||||
description: "ID of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const fingerprintColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "fingerprint",
|
||||
title: "Fingerprint",
|
||||
description: "Fingerprint of the exception",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const spanNameColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "spanName",
|
||||
title: "Span Name",
|
||||
description: "Name of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const attributesColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
super({
|
||||
tableName: "ExceptionItem",
|
||||
tableEngine: AnalyticsTableEngine.MergeTree,
|
||||
@@ -45,330 +371,22 @@ export default class ExceptionInstance extends AnalyticsBaseModel {
|
||||
},
|
||||
crudApiPath: new Route("/exceptions"),
|
||||
tableColumns: [
|
||||
new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "serviceId",
|
||||
title: "Service ID",
|
||||
description: "ID of the Service which created the log",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "time",
|
||||
title: "Time",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "timeUnixNano",
|
||||
title: "Time (in Unix Nano)",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "exceptionType",
|
||||
title: "Exception Type",
|
||||
description: "Exception Type", // Examples: java.net.ConnectException; OSError; etc.
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "stackTrace",
|
||||
title: "Stack Trace",
|
||||
description: "Exception Stack Trace", // Examples: Division by zero; Can't convert 'int' object to str implicitly
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "message",
|
||||
title: "Exception Message",
|
||||
description: "Exception Message", // Examples: Division by zero; Can't convert 'int' object to str implicitly
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "spanStatusCode",
|
||||
title: "Span Status Code",
|
||||
description: "Span Status Code",
|
||||
required: false,
|
||||
type: TableColumnType.Number,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "escaped",
|
||||
title: "Exception Escaped",
|
||||
description: "Exception Escaped", // SHOULD be set to true if the exception event is recorded at a point where it is known that the exception is escaping the scope of the span.
|
||||
required: false,
|
||||
type: TableColumnType.Boolean,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "traceId",
|
||||
title: "Trace ID",
|
||||
description: "ID of the trace",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "spanId",
|
||||
title: "Span ID",
|
||||
description: "ID of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "fingerprint",
|
||||
title: "Fingerprint",
|
||||
description: "Fingerprint of the exception",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "spanName",
|
||||
title: "Span Name",
|
||||
description: "Name of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryException,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryException,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
projectIdColumn,
|
||||
serviceIdColumn,
|
||||
timeColumn,
|
||||
timeUnixNanoColumn,
|
||||
exceptionTypeColumn,
|
||||
stackTraceColumn,
|
||||
messageColumn,
|
||||
spanStatusCodeColumn,
|
||||
escapedColumn,
|
||||
traceIdColumn,
|
||||
spanIdColumn,
|
||||
fingerprintColumn,
|
||||
spanNameColumn,
|
||||
attributesColumn,
|
||||
],
|
||||
projections: [],
|
||||
sortKeys: ["projectId", "time", "serviceId", "fingerprint"],
|
||||
primaryKeys: ["projectId", "time", "serviceId", "fingerprint"],
|
||||
partitionKey: "sipHash64(projectId) % 16",
|
||||
|
||||
@@ -2,7 +2,6 @@ import AnalyticsBaseModel from "./AnalyticsBaseModel/AnalyticsBaseModel";
|
||||
import Log from "./Log";
|
||||
import Metric from "./Metric";
|
||||
import Span from "./Span";
|
||||
import TelemetryAttribute from "./TelemetryAttribute";
|
||||
import ExceptionInstance from "./ExceptionInstance";
|
||||
import MonitorLog from "./MonitorLog";
|
||||
|
||||
@@ -10,7 +9,6 @@ const AnalyticsModels: Array<{ new (): AnalyticsBaseModel }> = [
|
||||
Log,
|
||||
Span,
|
||||
Metric,
|
||||
TelemetryAttribute,
|
||||
ExceptionInstance,
|
||||
MonitorLog,
|
||||
];
|
||||
|
||||
@@ -10,6 +10,264 @@ import LogSeverity from "../../Types/Log/LogSeverity";
|
||||
|
||||
export default class Log extends AnalyticsBaseModel {
|
||||
public constructor() {
|
||||
const projectIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const serviceIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "serviceId",
|
||||
title: "Service ID",
|
||||
description: "ID of the Service which created the log",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const timeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "time",
|
||||
title: "Time",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const timeUnixNanoColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "timeUnixNano",
|
||||
title: "Time (in Unix Nano)",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const severityTextColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "severityText",
|
||||
title: "Severity Text",
|
||||
description: "Log Severity Text",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const severityNumberColumn: AnalyticsTableColumn = new AnalyticsTableColumn(
|
||||
{
|
||||
key: "severityNumber",
|
||||
title: "Severity Number",
|
||||
description: "Log Severity Number",
|
||||
required: true,
|
||||
type: TableColumnType.Number,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const attributesColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const attributeKeysColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "attributeKeys",
|
||||
title: "Attribute Keys",
|
||||
description: "Attribute keys extracted from attributes",
|
||||
required: true,
|
||||
defaultValue: [],
|
||||
type: TableColumnType.ArrayText,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const traceIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "traceId",
|
||||
title: "Trace ID",
|
||||
description: "ID of the trace",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const spanIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "spanId",
|
||||
title: "Span ID",
|
||||
description: "ID of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const bodyColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "body",
|
||||
title: "Log Body",
|
||||
description: "Body of the Log",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
super({
|
||||
tableName: "LogItem",
|
||||
tableEngine: AnalyticsTableEngine.MergeTree,
|
||||
@@ -43,238 +301,19 @@ export default class Log extends AnalyticsBaseModel {
|
||||
pluralName: "Logs",
|
||||
crudApiPath: new Route("/logs"),
|
||||
tableColumns: [
|
||||
new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "serviceId",
|
||||
title: "Service ID",
|
||||
description: "ID of the Service which created the log",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "time",
|
||||
title: "Time",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "timeUnixNano",
|
||||
title: "Time (in Unix Nano)",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "severityText",
|
||||
title: "Severity Text",
|
||||
description: "Log Severity Text",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "severityNumber",
|
||||
title: "Severity Number",
|
||||
description: "Log Severity Number",
|
||||
required: true,
|
||||
type: TableColumnType.Number,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "traceId",
|
||||
title: "Trace ID",
|
||||
description: "ID of the trace",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "spanId",
|
||||
title: "Span ID",
|
||||
description: "ID of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "body",
|
||||
title: "Log Body",
|
||||
description: "Body of the Log",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
projectIdColumn,
|
||||
serviceIdColumn,
|
||||
timeColumn,
|
||||
timeUnixNanoColumn,
|
||||
severityTextColumn,
|
||||
severityNumberColumn,
|
||||
attributesColumn,
|
||||
attributeKeysColumn,
|
||||
traceIdColumn,
|
||||
spanIdColumn,
|
||||
bodyColumn,
|
||||
],
|
||||
projections: [],
|
||||
sortKeys: ["projectId", "time", "serviceId"],
|
||||
primaryKeys: ["projectId", "time", "serviceId"],
|
||||
partitionKey: "sipHash64(projectId) % 16",
|
||||
@@ -345,6 +384,14 @@ export default class Log extends AnalyticsBaseModel {
|
||||
this.setColumnValue("attributes", v);
|
||||
}
|
||||
|
||||
public get attributeKeys(): Array<string> | undefined {
|
||||
return this.getColumnValue("attributeKeys") as Array<string> | undefined;
|
||||
}
|
||||
|
||||
public set attributeKeys(v: Array<string> | undefined) {
|
||||
this.setColumnValue("attributeKeys", v);
|
||||
}
|
||||
|
||||
public get traceId(): string | undefined {
|
||||
return this.getColumnValue("traceId") as string | undefined;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,481 @@ export enum ServiceType {
|
||||
|
||||
export default class Metric extends AnalyticsBaseModel {
|
||||
public constructor() {
|
||||
const projectIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
// this can also be the monitor id or the telemetry service id.
|
||||
const serviceIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "serviceId",
|
||||
title: "Service ID",
|
||||
description: "ID of the Service which created the Metric",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
// this can also be the monitor id or the telemetry service id.
|
||||
const serviceTypeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "serviceType",
|
||||
title: "Service Type",
|
||||
description: "Type of the service that this telemetry belongs to",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
// add name and description
|
||||
const nameColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "name",
|
||||
title: "Name",
|
||||
description: "Name of the Metric",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const aggregationTemporalityColumn: AnalyticsTableColumn =
|
||||
new AnalyticsTableColumn({
|
||||
key: "aggregationTemporality",
|
||||
title: "Aggregation Temporality",
|
||||
description: "Aggregation Temporality of this Metric",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const metricPointTypeColumn: AnalyticsTableColumn =
|
||||
new AnalyticsTableColumn({
|
||||
key: "metricPointType",
|
||||
title: "Metric Point Type",
|
||||
description: "Metric Point Type of this Metric",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
// this is end time.
|
||||
const timeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "time",
|
||||
title: "Time",
|
||||
description: "When did the Metric happen?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const startTimeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "startTime",
|
||||
title: "Start Time",
|
||||
description: "When did the Metric happen?",
|
||||
required: false,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
// end time.
|
||||
const timeUnixNanoColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "timeUnixNano",
|
||||
title: "Time (in Unix Nano)",
|
||||
description: "When did the Metric happen?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const startTimeUnixNanoColumn: AnalyticsTableColumn =
|
||||
new AnalyticsTableColumn({
|
||||
key: "startTimeUnixNano",
|
||||
title: "Start Time (in Unix Nano)",
|
||||
description: "When did the Metric happen?",
|
||||
required: false,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const attributesColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
type: TableColumnType.JSON,
|
||||
defaultValue: {},
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const attributeKeysColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "attributeKeys",
|
||||
title: "Attribute Keys",
|
||||
description: "Attribute keys extracted from attributes",
|
||||
required: true,
|
||||
defaultValue: [],
|
||||
type: TableColumnType.ArrayText,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const isMonotonicColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "isMonotonic",
|
||||
title: "Is Monotonic",
|
||||
description: "Is Monotonic",
|
||||
required: false,
|
||||
type: TableColumnType.Boolean,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const countColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "count",
|
||||
title: "Count",
|
||||
description: "Count",
|
||||
required: false,
|
||||
type: TableColumnType.Number,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const sumColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "sum",
|
||||
title: "Sum",
|
||||
description: "Sum",
|
||||
required: false,
|
||||
type: TableColumnType.Decimal,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const valueColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "value",
|
||||
title: "Value",
|
||||
description: "Value",
|
||||
required: false,
|
||||
type: TableColumnType.Decimal,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const minColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "min",
|
||||
title: "Min",
|
||||
description: "Min",
|
||||
required: false,
|
||||
type: TableColumnType.Decimal,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const maxColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "max",
|
||||
title: "Max",
|
||||
description: "Max",
|
||||
required: false,
|
||||
type: TableColumnType.Decimal,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const bucketCountsColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "bucketCounts",
|
||||
title: "Bucket Counts",
|
||||
description: "Bucket Counts",
|
||||
required: true,
|
||||
defaultValue: [],
|
||||
type: TableColumnType.ArrayNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const explicitBoundsColumn: AnalyticsTableColumn = new AnalyticsTableColumn(
|
||||
{
|
||||
key: "explicitBounds",
|
||||
title: "Explicit Bonds",
|
||||
description: "Explicit Bonds",
|
||||
required: true,
|
||||
defaultValue: [],
|
||||
type: TableColumnType.ArrayNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
super({
|
||||
tableName: "MetricItem",
|
||||
tableEngine: AnalyticsTableEngine.MergeTree,
|
||||
@@ -61,453 +536,28 @@ export default class Metric extends AnalyticsBaseModel {
|
||||
],
|
||||
},
|
||||
tableColumns: [
|
||||
new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
// this can also be the monitor id or the telemetry service id.
|
||||
new AnalyticsTableColumn({
|
||||
key: "serviceId",
|
||||
title: "Service ID",
|
||||
description: "ID of the Service which created the Metric",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
// this can also be the monitor id or the telemetry service id.
|
||||
new AnalyticsTableColumn({
|
||||
key: "serviceType",
|
||||
title: "Service Type",
|
||||
description: "Type of the service that this telemetry belongs to",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
// add name and description
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "name",
|
||||
title: "Name",
|
||||
description: "Name of the Metric",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "aggregationTemporality",
|
||||
title: "Aggregation Temporality",
|
||||
description: "Aggregation Temporality of this Metric",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "metricPointType",
|
||||
title: "Metric Point Type",
|
||||
description: "Metric Point Type of this Metric",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
// this is end time.
|
||||
new AnalyticsTableColumn({
|
||||
key: "time",
|
||||
title: "Time",
|
||||
description: "When did the Metric happen?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "startTime",
|
||||
title: "Start Time",
|
||||
description: "When did the Metric happen?",
|
||||
required: false,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
// end time.
|
||||
new AnalyticsTableColumn({
|
||||
key: "timeUnixNano",
|
||||
title: "Time (in Unix Nano)",
|
||||
description: "When did the Metric happen?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "startTimeUnixNano",
|
||||
title: "Start Time (in Unix Nano)",
|
||||
description: "When did the Metric happen?",
|
||||
required: false,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
type: TableColumnType.JSON,
|
||||
defaultValue: {},
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "isMonotonic",
|
||||
title: "Is Monotonic",
|
||||
description: "Is Monotonic",
|
||||
required: false,
|
||||
type: TableColumnType.Boolean,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "count",
|
||||
title: "Count",
|
||||
description: "Count",
|
||||
required: false,
|
||||
type: TableColumnType.Number,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "sum",
|
||||
title: "Sum",
|
||||
description: "Sum",
|
||||
required: false,
|
||||
type: TableColumnType.Decimal,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "value",
|
||||
title: "Value",
|
||||
description: "Value",
|
||||
required: false,
|
||||
type: TableColumnType.Decimal,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "min",
|
||||
title: "Min",
|
||||
description: "Min",
|
||||
required: false,
|
||||
type: TableColumnType.Decimal,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "max",
|
||||
title: "Max",
|
||||
description: "Max",
|
||||
required: false,
|
||||
type: TableColumnType.Decimal,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "bucketCounts",
|
||||
title: "Bucket Counts",
|
||||
description: "Bucket Counts",
|
||||
required: true,
|
||||
defaultValue: [],
|
||||
type: TableColumnType.ArrayNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "explicitBounds",
|
||||
title: "Explicit Bonds",
|
||||
description: "Explicit Bonds",
|
||||
required: true,
|
||||
defaultValue: [],
|
||||
type: TableColumnType.ArrayNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
projectIdColumn,
|
||||
serviceIdColumn,
|
||||
serviceTypeColumn,
|
||||
nameColumn,
|
||||
aggregationTemporalityColumn,
|
||||
metricPointTypeColumn,
|
||||
timeColumn,
|
||||
startTimeColumn,
|
||||
timeUnixNanoColumn,
|
||||
startTimeUnixNanoColumn,
|
||||
attributesColumn,
|
||||
attributeKeysColumn,
|
||||
isMonotonicColumn,
|
||||
countColumn,
|
||||
sumColumn,
|
||||
valueColumn,
|
||||
minColumn,
|
||||
maxColumn,
|
||||
bucketCountsColumn,
|
||||
explicitBoundsColumn,
|
||||
],
|
||||
projections: [],
|
||||
sortKeys: ["projectId", "time", "serviceId"],
|
||||
primaryKeys: ["projectId", "time", "serviceId"],
|
||||
partitionKey: "sipHash64(projectId) % 16",
|
||||
@@ -590,6 +640,14 @@ export default class Metric extends AnalyticsBaseModel {
|
||||
this.setColumnValue("attributes", v);
|
||||
}
|
||||
|
||||
public get attributeKeys(): Array<string> | undefined {
|
||||
return this.getColumnValue("attributeKeys") as Array<string> | undefined;
|
||||
}
|
||||
|
||||
public set attributeKeys(v: Array<string> | undefined) {
|
||||
this.setColumnValue("attributeKeys", v);
|
||||
}
|
||||
|
||||
public get startTime(): Date | undefined {
|
||||
return this.getColumnValue("startTime") as Date | undefined;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,100 @@ import Permission from "../../Types/Permission";
|
||||
|
||||
export default class MonitorLog extends AnalyticsBaseModel {
|
||||
public constructor() {
|
||||
const projectIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectMonitor,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectMonitor,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const monitorIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "monitorId",
|
||||
title: "Monitor ID",
|
||||
description: "ID of the monitor which this logs belongs to",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectMonitor,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectMonitor,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const timeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "time",
|
||||
title: "Time",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectMonitor,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectMonitor,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const logBodyColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "logBody",
|
||||
title: "Log Body",
|
||||
description: "The body of the log",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectMonitor,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectMonitor,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
super({
|
||||
tableName: "MonitorLog",
|
||||
tableEngine: AnalyticsTableEngine.MergeTree,
|
||||
@@ -42,100 +136,12 @@ export default class MonitorLog extends AnalyticsBaseModel {
|
||||
pluralName: "Monitor Logs",
|
||||
crudApiPath: new Route("/monitor-log"),
|
||||
tableColumns: [
|
||||
new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectMonitor,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectMonitor,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "monitorId",
|
||||
title: "Monitor ID",
|
||||
description: "ID of the monitor which this logs belongs to",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectMonitor,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectMonitor,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "time",
|
||||
title: "Time",
|
||||
description: "When was the log created?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectMonitor,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectMonitor,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "logBody",
|
||||
title: "Log Body",
|
||||
description: "The body of the log",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectMonitor,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectMonitor,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
projectIdColumn,
|
||||
monitorIdColumn,
|
||||
timeColumn,
|
||||
logBodyColumn,
|
||||
],
|
||||
projections: [],
|
||||
sortKeys: ["projectId", "time", "monitorId"],
|
||||
primaryKeys: ["projectId", "time", "monitorId"],
|
||||
partitionKey: "sipHash64(projectId) % 16",
|
||||
|
||||
@@ -41,6 +41,451 @@ export interface SpanLink {
|
||||
|
||||
export default class Span extends AnalyticsBaseModel {
|
||||
public constructor() {
|
||||
const projectIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const serviceIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "serviceId",
|
||||
title: "Service ID",
|
||||
description: "ID of the Service which created the log",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const startTimeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "startTime",
|
||||
title: "Start Time",
|
||||
description: "When did the span start?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const endTimeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "endTime",
|
||||
title: "End Time",
|
||||
description: "When did the span end?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const startTimeUnixNanoColumn: AnalyticsTableColumn =
|
||||
new AnalyticsTableColumn({
|
||||
key: "startTimeUnixNano",
|
||||
title: "Start Time in Unix Nano",
|
||||
description: "When did the span start?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const durationUnixNanoColumn: AnalyticsTableColumn =
|
||||
new AnalyticsTableColumn({
|
||||
key: "durationUnixNano",
|
||||
title: "Duration in Unix Nano",
|
||||
description: "How long did the span last?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const endTimeUnixNanoColumn: AnalyticsTableColumn =
|
||||
new AnalyticsTableColumn({
|
||||
key: "endTimeUnixNano",
|
||||
title: "End Time",
|
||||
description: "When did the span end?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const traceIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "traceId",
|
||||
title: "Trace ID",
|
||||
description: "ID of the trace",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const spanIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "spanId",
|
||||
title: "Span ID",
|
||||
description: "ID of the span",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const parentSpanIdColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "parentSpanId",
|
||||
title: "Parent Span ID",
|
||||
description: "ID of the parent span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const traceStateColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "traceState",
|
||||
title: "Trace State",
|
||||
description: "Trace State",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const attributesColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const attributeKeysColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "attributeKeys",
|
||||
title: "Attribute Keys",
|
||||
description: "Attribute keys extracted from attributes",
|
||||
required: true,
|
||||
defaultValue: [],
|
||||
type: TableColumnType.ArrayText,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const eventsColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "events",
|
||||
title: "Events",
|
||||
description: "Span Events",
|
||||
required: true,
|
||||
defaultValue: [],
|
||||
type: TableColumnType.JSONArray,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const linksColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "links",
|
||||
title: "Links",
|
||||
description: "Span Links",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const statusCodeColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "statusCode",
|
||||
title: "Status Code",
|
||||
description: "Status Code",
|
||||
required: false,
|
||||
type: TableColumnType.Number,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const statusMessageColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "statusMessage",
|
||||
title: "Status Message",
|
||||
description: "Status Message",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const nameColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "name",
|
||||
title: "Name",
|
||||
description: "Name of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
const kindColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
||||
key: "kind",
|
||||
title: "Kind",
|
||||
description: "Kind of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
});
|
||||
|
||||
super({
|
||||
tableName: "SpanItem",
|
||||
tableEngine: AnalyticsTableEngine.MergeTree,
|
||||
@@ -74,424 +519,27 @@ export default class Span extends AnalyticsBaseModel {
|
||||
],
|
||||
},
|
||||
tableColumns: [
|
||||
new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "serviceId",
|
||||
title: "Service ID",
|
||||
description: "ID of the Service which created the log",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "startTime",
|
||||
title: "Start Time",
|
||||
description: "When did the span start?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "endTime",
|
||||
title: "End Time",
|
||||
description: "When did the span end?",
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "startTimeUnixNano",
|
||||
title: "Start Time in Unix Nano",
|
||||
description: "When did the span start?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "durationUnixNano",
|
||||
title: "Duration in Unix Nano",
|
||||
description: "How long did the span last?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "endTimeUnixNano",
|
||||
title: "End Time",
|
||||
description: "When did the span end?",
|
||||
required: true,
|
||||
type: TableColumnType.LongNumber,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "traceId",
|
||||
title: "Trace ID",
|
||||
description: "ID of the trace",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "spanId",
|
||||
title: "Span ID",
|
||||
description: "ID of the span",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "parentSpanId",
|
||||
title: "Parent Span ID",
|
||||
description: "ID of the parent span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "traceState",
|
||||
title: "Trace State",
|
||||
description: "Trace State",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "events",
|
||||
title: "Events",
|
||||
description: "Span Events",
|
||||
required: true,
|
||||
defaultValue: [],
|
||||
type: TableColumnType.JSONArray,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "links",
|
||||
title: "Links",
|
||||
description: "Span Links",
|
||||
required: true,
|
||||
defaultValue: {},
|
||||
type: TableColumnType.JSON,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "statusCode",
|
||||
title: "Status Code",
|
||||
description: "Status Code",
|
||||
required: false,
|
||||
type: TableColumnType.Number,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "statusMessage",
|
||||
title: "Status Message",
|
||||
description: "Status Message",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "name",
|
||||
title: "Name",
|
||||
description: "Name of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "kind",
|
||||
title: "Kind",
|
||||
description: "Kind of the span",
|
||||
required: false,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
projectIdColumn,
|
||||
serviceIdColumn,
|
||||
startTimeColumn,
|
||||
endTimeColumn,
|
||||
startTimeUnixNanoColumn,
|
||||
durationUnixNanoColumn,
|
||||
endTimeUnixNanoColumn,
|
||||
traceIdColumn,
|
||||
spanIdColumn,
|
||||
parentSpanIdColumn,
|
||||
traceStateColumn,
|
||||
attributesColumn,
|
||||
attributeKeysColumn,
|
||||
eventsColumn,
|
||||
linksColumn,
|
||||
statusCodeColumn,
|
||||
statusMessageColumn,
|
||||
nameColumn,
|
||||
kindColumn,
|
||||
],
|
||||
projections: [],
|
||||
sortKeys: ["projectId", "startTime", "serviceId", "traceId"],
|
||||
primaryKeys: ["projectId", "startTime", "serviceId", "traceId"],
|
||||
partitionKey: "sipHash64(projectId) % 16",
|
||||
@@ -610,6 +658,14 @@ export default class Span extends AnalyticsBaseModel {
|
||||
this.setColumnValue("attributes", v);
|
||||
}
|
||||
|
||||
public get attributeKeys(): Array<string> | undefined {
|
||||
return this.getColumnValue("attributeKeys") as Array<string> | undefined;
|
||||
}
|
||||
|
||||
public set attributeKeys(v: Array<string> | undefined) {
|
||||
this.setColumnValue("attributeKeys", v);
|
||||
}
|
||||
|
||||
public get events(): Array<SpanEvent> | undefined {
|
||||
return this.getColumnValue("events") as Array<SpanEvent> | undefined;
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
import AnalyticsBaseModel from "./AnalyticsBaseModel/AnalyticsBaseModel";
|
||||
import Route from "../../Types/API/Route";
|
||||
import AnalyticsTableEngine from "../../Types/AnalyticsDatabase/AnalyticsTableEngine";
|
||||
import AnalyticsTableColumn from "../../Types/AnalyticsDatabase/TableColumn";
|
||||
import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
|
||||
import TelemetryType from "../../Types/Telemetry/TelemetryType";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import Permission from "../../Types/Permission";
|
||||
|
||||
export default class TelemetryAttribute extends AnalyticsBaseModel {
|
||||
public constructor() {
|
||||
super({
|
||||
tableName: "TelemetryAttribute",
|
||||
tableEngine: AnalyticsTableEngine.MergeTree,
|
||||
singularName: "Telemetry Attribute",
|
||||
pluralName: "Telemetry Attributes",
|
||||
crudApiPath: new Route("/telemetry-attributes"),
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
Permission.ReadTelemetryServiceMetrics,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateTelemetryServiceTraces,
|
||||
Permission.CreateTelemetryServiceLog,
|
||||
Permission.CreateTelemetryServiceMetrics,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditTelemetryServiceTraces,
|
||||
Permission.EditTelemetryServiceLog,
|
||||
Permission.EditTelemetryServiceMetrics,
|
||||
],
|
||||
delete: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.DeleteTelemetryServiceTraces,
|
||||
Permission.DeleteTelemetryServiceLog,
|
||||
Permission.DeleteTelemetryServiceMetrics,
|
||||
],
|
||||
},
|
||||
tableColumns: [
|
||||
new AnalyticsTableColumn({
|
||||
key: "projectId",
|
||||
title: "Project ID",
|
||||
description: "ID of project",
|
||||
required: true,
|
||||
type: TableColumnType.ObjectID,
|
||||
isTenantId: true,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
Permission.ReadTelemetryServiceMetrics,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditTelemetryServiceTraces,
|
||||
Permission.EditTelemetryServiceLog,
|
||||
Permission.EditTelemetryServiceMetrics,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "telemetryType",
|
||||
title: "Telemetry Type",
|
||||
description: "Type of telemetry",
|
||||
required: true,
|
||||
type: TableColumnType.Text,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
Permission.ReadTelemetryServiceMetrics,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditTelemetryServiceTraces,
|
||||
Permission.EditTelemetryServiceLog,
|
||||
Permission.EditTelemetryServiceMetrics,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
|
||||
new AnalyticsTableColumn({
|
||||
key: "attributes",
|
||||
title: "Attributes",
|
||||
description: "Attributes",
|
||||
required: true,
|
||||
type: TableColumnType.JSONArray,
|
||||
accessControl: {
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadTelemetryServiceTraces,
|
||||
Permission.ReadTelemetryServiceLog,
|
||||
Permission.ReadTelemetryServiceMetrics,
|
||||
],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditTelemetryServiceTraces,
|
||||
Permission.EditTelemetryServiceLog,
|
||||
Permission.EditTelemetryServiceMetrics,
|
||||
],
|
||||
update: [],
|
||||
},
|
||||
}),
|
||||
],
|
||||
sortKeys: ["projectId", "telemetryType"],
|
||||
primaryKeys: ["projectId", "telemetryType"],
|
||||
partitionKey: "sipHash64(projectId) % 16",
|
||||
});
|
||||
}
|
||||
|
||||
public get projectId(): ObjectID | undefined {
|
||||
return this.getColumnValue("projectId") as ObjectID | undefined;
|
||||
}
|
||||
|
||||
public set projectId(v: ObjectID | undefined) {
|
||||
this.setColumnValue("projectId", v);
|
||||
}
|
||||
|
||||
public get telemetryType(): TelemetryType | undefined {
|
||||
return this.getColumnValue("telemetryType") as TelemetryType | undefined;
|
||||
}
|
||||
|
||||
public set telemetryType(v: TelemetryType | undefined) {
|
||||
this.setColumnValue("telemetryType", v);
|
||||
}
|
||||
|
||||
public get attributes(): Array<string> | undefined {
|
||||
return this.getColumnValue("attributes") as Array<string> | undefined;
|
||||
}
|
||||
|
||||
public set attributes(v: Array<string> | undefined) {
|
||||
this.setColumnValue("attributes", v);
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,13 @@ import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccess
|
||||
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
|
||||
import ColumnLength from "../../Types/Database/ColumnLength";
|
||||
import ColumnType from "../../Types/Database/ColumnType";
|
||||
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
||||
import TableColumn from "../../Types/Database/TableColumn";
|
||||
import TableColumnType from "../../Types/Database/TableColumnType";
|
||||
import TableMetadata from "../../Types/Database/TableMetadata";
|
||||
import IconProp from "../../Types/Icon/IconProp";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import Route from "../../Types/API/Route";
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
||||
|
||||
@TableAccessControl({
|
||||
@@ -24,6 +26,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
||||
icon: IconProp.Lock,
|
||||
tableDescription: "HTTP Challege for Lets Encrypt Certificates",
|
||||
})
|
||||
@CrudApiEndpoint(new Route("/acme-challenge"))
|
||||
@Entity({
|
||||
name: "AcmeChallenge",
|
||||
})
|
||||
|
||||
@@ -301,7 +301,12 @@ export default class Domain extends BaseModel {
|
||||
public deletedByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectDomain,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
|
||||
104
Common/Models/DatabaseModels/EnterpriseLicense.ts
Normal file
104
Common/Models/DatabaseModels/EnterpriseLicense.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
||||
import Route from "../../Types/API/Route";
|
||||
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
|
||||
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
|
||||
import ColumnLength from "../../Types/Database/ColumnLength";
|
||||
import ColumnType from "../../Types/Database/ColumnType";
|
||||
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
||||
import TableColumn from "../../Types/Database/TableColumn";
|
||||
import TableColumnType from "../../Types/Database/TableColumnType";
|
||||
import TableMetadata from "../../Types/Database/TableMetadata";
|
||||
import IconProp from "../../Types/Icon/IconProp";
|
||||
import { Column, Entity, Index } from "typeorm";
|
||||
|
||||
@TableAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
delete: [],
|
||||
})
|
||||
@CrudApiEndpoint(new Route("/enterprise-license"))
|
||||
@TableMetadata({
|
||||
tableName: "EnterpriseLicense",
|
||||
singularName: "Enterprise License",
|
||||
pluralName: "Enterprise Licenses",
|
||||
icon: IconProp.Lock,
|
||||
tableDescription: "Enterprise license keys issued by OneUptime.",
|
||||
})
|
||||
@Entity({
|
||||
name: "EnterpriseLicense",
|
||||
})
|
||||
export default class EnterpriseLicense extends BaseModel {
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.ShortText,
|
||||
title: "Company Name",
|
||||
description: "Company name associated with this license.",
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
})
|
||||
public companyName?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.ShortText,
|
||||
title: "License Key",
|
||||
description: "Enterprise license key.",
|
||||
unique: true,
|
||||
})
|
||||
@Index({ unique: true })
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
unique: true,
|
||||
})
|
||||
public licenseKey?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.Date,
|
||||
title: "Expires At",
|
||||
description: "Expiration date of this license.",
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.Date,
|
||||
})
|
||||
public expiresAt?: Date = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
required: false,
|
||||
type: TableColumnType.Number,
|
||||
title: "Annual Contract Value",
|
||||
description: "Annual contract value (in USD) for this license.",
|
||||
})
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: ColumnType.Number,
|
||||
})
|
||||
public annualContractValue?: number = undefined;
|
||||
}
|
||||
@@ -352,6 +352,25 @@ export default class GlobalConfig extends GlobalConfigModel {
|
||||
})
|
||||
public metaWhatsAppAppSecret?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ShortText,
|
||||
title: "Meta WhatsApp Webhook Verify Token",
|
||||
description:
|
||||
"Verify token configured in Meta to validate webhook subscriptions.",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
nullable: true,
|
||||
unique: true,
|
||||
})
|
||||
public metaWhatsAppWebhookVerifyToken?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
@@ -478,4 +497,75 @@ export default class GlobalConfig extends GlobalConfigModel {
|
||||
transformer: Email.getDatabaseTransformer(),
|
||||
})
|
||||
public adminNotificationEmail?: Email = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ShortText,
|
||||
title: "Enterprise Company Name",
|
||||
description:
|
||||
"Company name associated with the validated enterprise license.",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
nullable: true,
|
||||
unique: true,
|
||||
})
|
||||
public enterpriseCompanyName?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ShortText,
|
||||
title: "Enterprise License Key",
|
||||
description: "Enterprise license key stored after successful validation.",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
nullable: true,
|
||||
unique: true,
|
||||
})
|
||||
public enterpriseLicenseKey?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.Date,
|
||||
title: "Enterprise License Expires At",
|
||||
description: "Expiration date of the validated enterprise license.",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Date,
|
||||
nullable: true,
|
||||
unique: true,
|
||||
})
|
||||
public enterpriseLicenseExpiresAt?: Date = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.VeryLongText,
|
||||
title: "Enterprise License Token",
|
||||
description: "Signed JWT returned from license validation.",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.VeryLongText,
|
||||
nullable: true,
|
||||
unique: true,
|
||||
})
|
||||
public enterpriseLicenseToken?: string = undefined;
|
||||
}
|
||||
|
||||
@@ -926,6 +926,39 @@ export default class Incident extends BaseModel {
|
||||
})
|
||||
public rootCause?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectIncident,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectIncident,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectIncident,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.Markdown,
|
||||
required: false,
|
||||
isDefaultValueColumn: false,
|
||||
title: "Postmortem Note",
|
||||
description: "Document the postmortem summary for this incident.",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Markdown,
|
||||
nullable: true,
|
||||
})
|
||||
public postmortemNote?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [
|
||||
|
||||
@@ -33,6 +33,7 @@ export enum IncidentFeedEventType {
|
||||
IncidentUpdated = "IncidentUpdated",
|
||||
RootCause = "RootCause",
|
||||
RemediationNotes = "RemediationNotes",
|
||||
PostmortemNote = "PostmortemNote",
|
||||
OwnerUserRemoved = "OwnerUserRemoved",
|
||||
OwnerTeamRemoved = "OwnerTeamRemoved",
|
||||
OnCallPolicy = "OnCallPolicy",
|
||||
|
||||
353
Common/Models/DatabaseModels/IncidentPostmortemTemplate.ts
Normal file
353
Common/Models/DatabaseModels/IncidentPostmortemTemplate.ts
Normal file
@@ -0,0 +1,353 @@
|
||||
import Project from "./Project";
|
||||
import User from "./User";
|
||||
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
||||
import Route from "../../Types/API/Route";
|
||||
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
|
||||
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
|
||||
import ColumnLength from "../../Types/Database/ColumnLength";
|
||||
import ColumnType from "../../Types/Database/ColumnType";
|
||||
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
||||
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
||||
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
||||
import TableColumn from "../../Types/Database/TableColumn";
|
||||
import TableColumnType from "../../Types/Database/TableColumnType";
|
||||
import TableMetadata from "../../Types/Database/TableMetadata";
|
||||
import TenantColumn from "../../Types/Database/TenantColumn";
|
||||
import IconProp from "../../Types/Icon/IconProp";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import Permission from "../../Types/Permission";
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
||||
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
|
||||
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
|
||||
|
||||
@TableBillingAccessControl({
|
||||
create: PlanType.Growth,
|
||||
read: PlanType.Growth,
|
||||
update: PlanType.Growth,
|
||||
delete: PlanType.Growth,
|
||||
})
|
||||
@EnableDocumentation()
|
||||
@TenantColumn("projectId")
|
||||
@TableAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateIncidentNoteTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadIncidentNoteTemplate,
|
||||
],
|
||||
delete: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.DeleteIncidentNoteTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditIncidentNoteTemplate,
|
||||
],
|
||||
})
|
||||
@CrudApiEndpoint(new Route("/incident-postmortem-template"))
|
||||
@Entity({
|
||||
name: "IncidentPostmortemTemplate",
|
||||
})
|
||||
@EnableWorkflow({
|
||||
create: true,
|
||||
delete: true,
|
||||
update: true,
|
||||
read: true,
|
||||
})
|
||||
@TableMetadata({
|
||||
tableName: "IncidentPostmortemTemplate",
|
||||
singularName: "Incident Postmortem Template",
|
||||
pluralName: "Incident Postmortem Templates",
|
||||
icon: IconProp.Book,
|
||||
tableDescription: "Manage postmortem templates for your incidents",
|
||||
})
|
||||
export default class IncidentPostmortemTemplate extends BaseModel {
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateIncidentNoteTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadIncidentNoteTemplate,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "projectId",
|
||||
type: TableColumnType.Entity,
|
||||
modelType: Project,
|
||||
title: "Project",
|
||||
description: "Relation to Project Resource in which this object belongs",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return Project;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "CASCADE",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "projectId" })
|
||||
public project?: Project = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateIncidentNoteTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadIncidentNoteTemplate,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
required: true,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Project ID",
|
||||
description: "ID of your OneUptime Project in which this object belongs",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: false,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public projectId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateIncidentNoteTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadIncidentNoteTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditIncidentNoteTemplate,
|
||||
],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
type: TableColumnType.Markdown,
|
||||
title: "Postmortem Note",
|
||||
description:
|
||||
"Markdown template used when documenting an incident postmortem.",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Markdown,
|
||||
nullable: false,
|
||||
unique: false,
|
||||
})
|
||||
public postmortemNote?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateIncidentNoteTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadIncidentNoteTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditIncidentNoteTemplate,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.ShortText,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Name",
|
||||
description: "Name of the Postmortem Template",
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
})
|
||||
public templateName?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateIncidentNoteTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadIncidentNoteTemplate,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditIncidentNoteTemplate,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.LongText,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Template Description",
|
||||
description: "Description of the Postmortem Template",
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.LongText,
|
||||
length: ColumnLength.LongText,
|
||||
})
|
||||
public templateDescription?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateIncidentNoteTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadIncidentNoteTemplate,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "createdByUserId",
|
||||
type: TableColumnType.Entity,
|
||||
modelType: User,
|
||||
title: "Created by User",
|
||||
description:
|
||||
"Relation to User who created this object (if this object was created by a User)",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "SET NULL",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "createdByUserId" })
|
||||
public createdByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateIncidentNoteTemplate,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadIncidentNoteTemplate,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
title: "Created by User ID",
|
||||
description:
|
||||
"User ID who created this object (if this object was created by a User)",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public createdByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "deletedByUserId",
|
||||
type: TableColumnType.Entity,
|
||||
title: "Deleted by User",
|
||||
modelType: User,
|
||||
description:
|
||||
"Relation to User who deleted this object (if this object was deleted by a User)",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
{
|
||||
cascade: false,
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "SET NULL",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "deletedByUserId" })
|
||||
public deletedByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
title: "Deleted by User ID",
|
||||
description:
|
||||
"User ID who deleted this object (if this object was deleted by a User)",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public deletedByUserId?: ObjectID = undefined;
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import IncidentFeed from "./IncidentFeed";
|
||||
import IncidentCustomField from "./IncidentCustomField";
|
||||
import IncidentInternalNote from "./IncidentInternalNote";
|
||||
import IncidentNoteTemplate from "./IncidentNoteTemplate";
|
||||
import IncidentPostmortemTemplate from "./IncidentPostmortemTemplate";
|
||||
import IncidentOwnerTeam from "./IncidentOwnerTeam";
|
||||
import IncidentOwnerUser from "./IncidentOwnerUser";
|
||||
import IncidentPublicNote from "./IncidentPublicNote";
|
||||
@@ -80,6 +81,7 @@ import ProjectSmtpConfig from "./ProjectSmtpConfig";
|
||||
//SSO
|
||||
import ProjectSSO from "./ProjectSso";
|
||||
import PromoCode from "./PromoCode";
|
||||
import EnterpriseLicense from "./EnterpriseLicense";
|
||||
import Reseller from "./Reseller";
|
||||
import ResellerPlan from "./ResellerPlan";
|
||||
// ScheduledMaintenances
|
||||
@@ -235,6 +237,7 @@ const AllModelTypes: Array<{
|
||||
IncidentOwnerUser,
|
||||
IncidentSeverity,
|
||||
IncidentNoteTemplate,
|
||||
IncidentPostmortemTemplate,
|
||||
|
||||
AlertState,
|
||||
Alert,
|
||||
@@ -329,6 +332,7 @@ const AllModelTypes: Array<{
|
||||
ResellerPlan,
|
||||
|
||||
PromoCode,
|
||||
EnterpriseLicense,
|
||||
|
||||
GlobalConfig,
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ export default class Project extends TenantModel {
|
||||
Permission.UnAuthorizedSsoUser,
|
||||
Permission.ProjectUser,
|
||||
],
|
||||
update: [Permission.ProjectOwner],
|
||||
update: [Permission.ProjectOwner, Permission.ManageProjectBilling],
|
||||
})
|
||||
@TableColumn({ type: TableColumnType.ShortText })
|
||||
@Column({
|
||||
|
||||
@@ -2331,4 +2331,84 @@ export default class StatusPage extends BaseModel {
|
||||
create: PlanType.Free,
|
||||
})
|
||||
public ipWhitelist?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectStatusPage,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectStatusPage,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
isDefaultValueColumn: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: "Enable Embedded Overall Status Badge",
|
||||
description:
|
||||
"Enable embedded overall status badge that can be displayed on external websites?",
|
||||
defaultValue: false,
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Boolean,
|
||||
default: false,
|
||||
nullable: false,
|
||||
})
|
||||
@ColumnBillingAccessControl({
|
||||
read: PlanType.Free,
|
||||
update: PlanType.Growth,
|
||||
create: PlanType.Free,
|
||||
})
|
||||
public enableEmbeddedOverallStatus?: boolean = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectStatusPage,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectStatusPage,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectStatusPage,
|
||||
],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
type: TableColumnType.ShortText,
|
||||
required: false,
|
||||
title: "Embedded Overall Status Token",
|
||||
description:
|
||||
"Security token required to access the embedded overall status badge. This token must be provided in the URL.",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
nullable: true,
|
||||
})
|
||||
@ColumnBillingAccessControl({
|
||||
read: PlanType.Free,
|
||||
update: PlanType.Growth,
|
||||
create: PlanType.Free,
|
||||
})
|
||||
public embeddedOverallStatusToken?: string = undefined;
|
||||
}
|
||||
|
||||
@@ -290,6 +290,21 @@ export default class StatusPagePrivateUser extends BaseModel {
|
||||
nullable: true,
|
||||
unique: false,
|
||||
})
|
||||
public jwtRefreshToken?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({ type: TableColumnType.ShortText })
|
||||
@Column({
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
nullable: true,
|
||||
unique: false,
|
||||
})
|
||||
public resetPasswordToken?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
|
||||
@@ -8,7 +8,6 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
||||
import ColumnType from "../../Types/Database/ColumnType";
|
||||
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
||||
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
||||
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
||||
import TableColumn from "../../Types/Database/TableColumn";
|
||||
import TableColumnType from "../../Types/Database/TableColumnType";
|
||||
import TableMetadata from "../../Types/Database/TableMetadata";
|
||||
@@ -45,12 +44,6 @@ import TelemetryService from "./TelemetryService";
|
||||
Permission.EditTelemetryException,
|
||||
],
|
||||
})
|
||||
@EnableWorkflow({
|
||||
create: true,
|
||||
delete: true,
|
||||
update: true,
|
||||
read: true,
|
||||
})
|
||||
@CrudApiEndpoint(new Route("/telemetry-exception-status"))
|
||||
@TableMetadata({
|
||||
tableName: "TelemetryException",
|
||||
|
||||
@@ -39,7 +39,7 @@ export const DEFAULT_RETENTION_IN_DAYS: number = 15;
|
||||
pluralName: "Telemetry Usage Billings",
|
||||
icon: IconProp.Billing,
|
||||
tableDescription:
|
||||
"Stores historical usage billing data for your telemetry data like Logs, Metrics, and Traces.",
|
||||
"Stores historical usage billing data for your telemetry data like Logs, Metrics, Traces, and Exceptions.",
|
||||
})
|
||||
@Entity({
|
||||
name: "TelemetryUsageBilling",
|
||||
|
||||
@@ -37,7 +37,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
delete: [],
|
||||
update: [],
|
||||
@@ -66,7 +66,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -97,7 +97,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -122,7 +122,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -148,7 +148,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -175,7 +175,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -198,7 +198,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -222,7 +222,32 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@Index()
|
||||
@TableColumn({
|
||||
required: false,
|
||||
type: TableColumnType.ShortText,
|
||||
title: "WhatsApp Message ID",
|
||||
description: "Message ID returned by Meta's API",
|
||||
canReadOnRelationQuery: false,
|
||||
})
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
})
|
||||
public whatsAppMessageId?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -246,7 +271,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -273,7 +298,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -304,7 +329,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -329,7 +354,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -360,7 +385,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -385,7 +410,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -416,7 +441,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -441,7 +466,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -472,7 +497,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -498,7 +523,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -529,7 +554,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -554,7 +579,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -586,7 +611,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -612,7 +637,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -643,7 +668,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -668,7 +693,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -699,7 +724,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -725,7 +750,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -758,7 +783,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -784,7 +809,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@@ -816,7 +841,7 @@ export default class WhatsAppLog extends BaseModel {
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadSmsLog,
|
||||
Permission.ReadWhatsAppLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
|
||||
64
Common/Server/API/AcmeChallengeAPI.ts
Normal file
64
Common/Server/API/AcmeChallengeAPI.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import AcmeChallenge from "../../Models/DatabaseModels/AcmeChallenge";
|
||||
import NotFoundException from "../../Types/Exception/NotFoundException";
|
||||
import AcmeChallengeService, {
|
||||
Service as AcmeChallengeServiceType,
|
||||
} from "../Services/AcmeChallengeService";
|
||||
import Express, {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
ExpressRouter,
|
||||
NextFunction,
|
||||
} from "../Utils/Express";
|
||||
import Response from "../Utils/Response";
|
||||
import BaseAPI from "./BaseAPI";
|
||||
|
||||
export default class AcmeChallengeAPI extends BaseAPI<
|
||||
AcmeChallenge,
|
||||
AcmeChallengeServiceType
|
||||
> {
|
||||
private wellKnownRouter: ExpressRouter;
|
||||
|
||||
public constructor() {
|
||||
super(AcmeChallenge, AcmeChallengeService);
|
||||
|
||||
this.wellKnownRouter = Express.getRouter();
|
||||
|
||||
this.wellKnownRouter.get(
|
||||
"/acme-challenge/.well-known/:token",
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const challenge: AcmeChallenge | null =
|
||||
await AcmeChallengeService.findOneBy({
|
||||
query: {
|
||||
token: req.params["token"] as string,
|
||||
},
|
||||
select: {
|
||||
challenge: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!challenge) {
|
||||
return next(new NotFoundException("Challenge not found"));
|
||||
}
|
||||
|
||||
return Response.sendTextResponse(
|
||||
req,
|
||||
res,
|
||||
challenge.challenge as string,
|
||||
);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.router.use("/", this.wellKnownRouter);
|
||||
}
|
||||
|
||||
public getWellKnownRouter(): ExpressRouter {
|
||||
return this.wellKnownRouter;
|
||||
}
|
||||
}
|
||||
102
Common/Server/API/EnterpriseLicenseAPI.ts
Normal file
102
Common/Server/API/EnterpriseLicenseAPI.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import EnterpriseLicense from "../../Models/DatabaseModels/EnterpriseLicense";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import { JSONObject } from "../../Types/JSON";
|
||||
import EnterpriseLicenseService, {
|
||||
Service as EnterpriseLicenseServiceType,
|
||||
} from "../Services/EnterpriseLicenseService";
|
||||
import UserMiddleware from "../Middleware/UserAuthorization";
|
||||
import JSONWebToken from "../Utils/JsonWebToken";
|
||||
import Response from "../Utils/Response";
|
||||
import {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
NextFunction,
|
||||
} from "../Utils/Express";
|
||||
import BaseAPI from "./BaseAPI";
|
||||
// import { Host } from "../EnvironmentConfig";
|
||||
|
||||
export default class EnterpriseLicenseAPI extends BaseAPI<
|
||||
EnterpriseLicense,
|
||||
EnterpriseLicenseServiceType
|
||||
> {
|
||||
public constructor() {
|
||||
super(EnterpriseLicense, EnterpriseLicenseService);
|
||||
|
||||
this.router.post(
|
||||
`${new this.entityType().getCrudApiPath()?.toString()}/validate`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const licenseKey: string | undefined = req.body["licenseKey"];
|
||||
|
||||
if (!licenseKey) {
|
||||
throw new BadDataException("License key is required");
|
||||
}
|
||||
|
||||
//const serverHost: string = Host.toString();
|
||||
|
||||
/*
|
||||
* if (!serverHost.includes("oneuptime.com")) {
|
||||
* throw new BadDataException(
|
||||
* "Enterprise license validation is only available on oneuptime.com",
|
||||
* );
|
||||
* }
|
||||
*/
|
||||
|
||||
const license: EnterpriseLicense | null =
|
||||
await EnterpriseLicenseService.findOneBy({
|
||||
query: {
|
||||
licenseKey: licenseKey,
|
||||
},
|
||||
select: {
|
||||
companyName: true,
|
||||
expiresAt: true,
|
||||
licenseKey: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!license) {
|
||||
throw new BadDataException("License key is invalid");
|
||||
}
|
||||
|
||||
if (!license.expiresAt) {
|
||||
throw new BadDataException("License expiration is not set");
|
||||
}
|
||||
|
||||
const now: number = Date.now();
|
||||
const expiresAtMs: number = license.expiresAt.getTime();
|
||||
const secondsUntilExpiry: number = Math.floor(
|
||||
(expiresAtMs - now) / 1000,
|
||||
);
|
||||
|
||||
if (secondsUntilExpiry <= 0) {
|
||||
throw new BadDataException("License key has expired");
|
||||
}
|
||||
|
||||
const payload: JSONObject = {
|
||||
companyName: license.companyName || "",
|
||||
expiresAt: license.expiresAt.toISOString(),
|
||||
licenseKey: license.licenseKey || "",
|
||||
};
|
||||
|
||||
const token: string = JSONWebToken.signJsonPayload(
|
||||
payload,
|
||||
Math.max(secondsUntilExpiry, 1),
|
||||
);
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, {
|
||||
companyName: payload["companyName"] as string,
|
||||
expiresAt: payload["expiresAt"] as string,
|
||||
licenseKey: payload["licenseKey"] as string,
|
||||
token,
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,15 @@ import {
|
||||
import Response from "../Utils/Response";
|
||||
import BaseAPI from "./BaseAPI";
|
||||
import GlobalConfig from "../../Models/DatabaseModels/GlobalConfig";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import { JSONObject } from "../../Types/JSON";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import API from "../../Utils/API";
|
||||
import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
|
||||
import HTTPResponse from "../../Types/API/HTTPResponse";
|
||||
import PartialEntity from "../../Types/Database/PartialEntity";
|
||||
import { EnterpriseLicenseValidationUrl } from "../EnvironmentConfig";
|
||||
import UserMiddleware from "../Middleware/UserAuthorization";
|
||||
|
||||
export default class GlobalConfigAPI extends BaseAPI<
|
||||
GlobalConfig,
|
||||
@@ -45,5 +54,164 @@ export default class GlobalConfigAPI extends BaseAPI<
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.router.get(
|
||||
`${new this.entityType().getCrudApiPath()?.toString()}/license`,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const config: GlobalConfig | null =
|
||||
await GlobalConfigService.findOneById({
|
||||
id: ObjectID.getZeroObjectID(),
|
||||
select: {
|
||||
enterpriseCompanyName: true,
|
||||
enterpriseLicenseExpiresAt: true,
|
||||
enterpriseLicenseKey: true,
|
||||
enterpriseLicenseToken: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const responseBody: JSONObject = {
|
||||
companyName: config?.enterpriseCompanyName || null,
|
||||
expiresAt: config?.enterpriseLicenseExpiresAt
|
||||
? config.enterpriseLicenseExpiresAt.toISOString()
|
||||
: null,
|
||||
licenseKey: config?.enterpriseLicenseKey || null,
|
||||
token: config?.enterpriseLicenseToken || null,
|
||||
};
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, responseBody);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.router.post(
|
||||
`${new this.entityType().getCrudApiPath()?.toString()}/license`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const licenseKey: string =
|
||||
(req.body["licenseKey"] as string | undefined)?.trim() || "";
|
||||
|
||||
if (!licenseKey) {
|
||||
throw new BadDataException("License key is required");
|
||||
}
|
||||
|
||||
const validationResponse:
|
||||
| HTTPResponse<JSONObject>
|
||||
| HTTPErrorResponse = await API.post<JSONObject>({
|
||||
url: EnterpriseLicenseValidationUrl,
|
||||
data: {
|
||||
licenseKey,
|
||||
},
|
||||
});
|
||||
|
||||
if (!validationResponse.isSuccess()) {
|
||||
const errorMessage: string =
|
||||
validationResponse instanceof HTTPErrorResponse
|
||||
? validationResponse.message ||
|
||||
"Failed to validate license key."
|
||||
: "Failed to validate license key.";
|
||||
throw new BadDataException(errorMessage);
|
||||
}
|
||||
|
||||
const payload: JSONObject = validationResponse.data as JSONObject;
|
||||
|
||||
const companyNameRaw: string =
|
||||
(payload["companyName"] as string | undefined)?.trim() || "";
|
||||
const expiresAtRaw: string =
|
||||
(payload["expiresAt"] as string | undefined) || "";
|
||||
const licenseKeyRaw: string =
|
||||
(payload["licenseKey"] as string | undefined)?.trim() || licenseKey;
|
||||
const licenseToken: string =
|
||||
(payload["token"] as string | undefined) || "";
|
||||
|
||||
let licenseExpiry: Date | undefined = undefined;
|
||||
if (expiresAtRaw) {
|
||||
const parsedDate: Date = new Date(expiresAtRaw);
|
||||
|
||||
if (Number.isNaN(parsedDate.getTime())) {
|
||||
throw new BadDataException(
|
||||
"License expiration returned from server is invalid.",
|
||||
);
|
||||
}
|
||||
|
||||
licenseExpiry = parsedDate;
|
||||
}
|
||||
|
||||
const updatePayload: PartialEntity<GlobalConfig> = {
|
||||
enterpriseCompanyName: companyNameRaw || null,
|
||||
enterpriseLicenseKey: licenseKeyRaw || null,
|
||||
enterpriseLicenseExpiresAt: licenseExpiry || null,
|
||||
enterpriseLicenseToken: licenseToken || null,
|
||||
};
|
||||
|
||||
const globalConfigId: ObjectID = ObjectID.getZeroObjectID();
|
||||
|
||||
const existingConfig: GlobalConfig | null =
|
||||
await GlobalConfigService.findOneById({
|
||||
id: globalConfigId,
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
ignoreHooks: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingConfig) {
|
||||
await GlobalConfigService.updateOneById({
|
||||
id: globalConfigId,
|
||||
data: updatePayload,
|
||||
props: {
|
||||
isRoot: true,
|
||||
ignoreHooks: true,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const newConfig: GlobalConfig = new GlobalConfig();
|
||||
newConfig.id = globalConfigId;
|
||||
|
||||
if (companyNameRaw) {
|
||||
newConfig.enterpriseCompanyName = companyNameRaw;
|
||||
}
|
||||
|
||||
if (licenseKeyRaw) {
|
||||
newConfig.enterpriseLicenseKey = licenseKeyRaw;
|
||||
}
|
||||
|
||||
if (licenseToken) {
|
||||
newConfig.enterpriseLicenseToken = licenseToken;
|
||||
}
|
||||
|
||||
if (licenseExpiry) {
|
||||
newConfig.enterpriseLicenseExpiresAt = licenseExpiry;
|
||||
}
|
||||
|
||||
await GlobalConfigService.create({
|
||||
data: newConfig,
|
||||
props: {
|
||||
isRoot: true,
|
||||
ignoreHooks: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, {
|
||||
companyName: companyNameRaw || null,
|
||||
expiresAt: licenseExpiry ? licenseExpiry.toISOString() : null,
|
||||
licenseKey: licenseKeyRaw || null,
|
||||
token: licenseToken || null,
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export default class MicrosoftTeamsAPI {
|
||||
"https://developer.microsoft.com/json-schemas/teams/v1.23/MicrosoftTeams.schema.json",
|
||||
manifestVersion: "1.23",
|
||||
version: AppVersion.toLowerCase().includes("unknown")
|
||||
? "1.3.0"
|
||||
? "1.5.0"
|
||||
: AppVersion,
|
||||
id: MicrosoftTeamsAppClientId,
|
||||
developer: {
|
||||
@@ -75,12 +75,13 @@ export default class MicrosoftTeamsAPI {
|
||||
},
|
||||
description: {
|
||||
short: "Complete open-source monitoring and observability platform. ",
|
||||
full: `OneUptime is a comprehensive solution for monitoring and managing your online services. Whether you need to check the availability of your website, dashboard, API, or any other online resource, OneUptime can alert your team when downtime happens and keep your customers informed with a status page. OneUptime also helps you handle incidents, set up on-call rotations, run tests, secure your services, analyze logs, track performance, and debug errors.
|
||||
full: `<p>OneUptime is a comprehensive solution for monitoring and managing your online services. Whether you need to check the availability of your website, dashboard, API, or any other online resource, OneUptime can alert your team when downtime happens and keep your customers informed with a status page. OneUptime also helps you handle incidents, set up on-call rotations, run tests, secure your services, analyze logs, track performance, and debug errors.</p>
|
||||
|
||||
In order to use the app, you need to have an active account with OneUptime at https://oneuptime.com. Please send an email to support@oneupitme.com if you need more details.
|
||||
<p>In order to use the app, you need to have an active account with <a href="https://oneuptime.com" target="_blank">OneUptime</a>. Please send an email to <a href="mailto:support@oneuptime.com">support@oneuptime.com</a> if you need more details.</p>
|
||||
|
||||
Create a new OneUptime Account: If you wish to sign up for a new account, you can do so at https://oneuptime.com and click on Sign up.
|
||||
Help and Support: You can reach out to help and support here: https://oneuptime.com/support or contact support@oneuptime.com
|
||||
<p><strong>Create a new OneUptime Account:</strong> If you wish to sign up for a new account, you can do so by visiting <a href="https://oneuptime.com" target="_blank">OneUptime Sign Up</a>.</p>
|
||||
|
||||
<p><strong>Help and Support:</strong> You can reach out to help and support via <a href="https://oneuptime.com/support" target="_blank">Support Page</a> or contact <a href="mailto:support@oneuptime.com">support@oneuptime.com</a>.</p>
|
||||
`,
|
||||
},
|
||||
// Default to size-specific names; route will adjust if fallbacks are used
|
||||
@@ -622,9 +623,8 @@ Help and Support: You can reach out to help and support here: https://oneuptime.
|
||||
projectId: new ObjectID(projectId),
|
||||
workspaceType: WorkspaceType.MicrosoftTeams,
|
||||
});
|
||||
const existingTenant: string | undefined = (
|
||||
existingAuth?.miscData as any
|
||||
)?.tenantId;
|
||||
const existingTenant: string | undefined =
|
||||
existingAuth?.workspaceProjectId;
|
||||
if (existingTenant) {
|
||||
tenantForConsent = existingTenant;
|
||||
}
|
||||
|
||||
@@ -20,11 +20,72 @@ import PositiveNumber from "../../Types/PositiveNumber";
|
||||
import Project from "../../Models/DatabaseModels/Project";
|
||||
import Reseller from "../../Models/DatabaseModels/Reseller";
|
||||
import TeamMember from "../../Models/DatabaseModels/TeamMember";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import Permission, { UserPermission } from "../../Types/Permission";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import { JSONObject } from "../../Types/JSON";
|
||||
|
||||
export default class ProjectAPI extends BaseAPI<Project, ProjectServiceType> {
|
||||
public constructor() {
|
||||
super(Project, ProjectService);
|
||||
|
||||
this.router.put(
|
||||
`${new this.entityType().getCrudApiPath()?.toString()}/:id/change-plan`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
if (!IsBillingEnabled) {
|
||||
throw new BadDataException(
|
||||
"Billing is not enabled for this server",
|
||||
);
|
||||
}
|
||||
|
||||
const projectId: ObjectID = new ObjectID(req.params["id"] as string);
|
||||
|
||||
const body: JSONObject = (req.body as JSONObject) || {};
|
||||
const data: JSONObject = (body["data"] as JSONObject) || {};
|
||||
const paymentProviderPlanId: string | undefined = data[
|
||||
"paymentProviderPlanId"
|
||||
] as string | undefined;
|
||||
|
||||
if (!paymentProviderPlanId) {
|
||||
throw new BadDataException("Plan ID is required to change plan");
|
||||
}
|
||||
|
||||
const permissions: Array<UserPermission> =
|
||||
await this.getPermissionsForTenant(req);
|
||||
|
||||
const hasBillingPermission: boolean =
|
||||
permissions.filter((permission: UserPermission) => {
|
||||
return (
|
||||
permission.permission.toString() ===
|
||||
Permission.ProjectOwner.toString() ||
|
||||
permission.permission.toString() ===
|
||||
Permission.ManageProjectBilling.toString()
|
||||
);
|
||||
}).length > 0;
|
||||
|
||||
if (
|
||||
!hasBillingPermission &&
|
||||
!(req as OneUptimeRequest).userAuthorization?.isMasterAdmin
|
||||
) {
|
||||
throw new BadDataException(
|
||||
`You need ${Permission.ProjectOwner} or ${Permission.ManageProjectBilling} permission to change project plan`,
|
||||
);
|
||||
}
|
||||
|
||||
await ProjectService.changePlan({
|
||||
projectId: projectId,
|
||||
paymentProviderPlanId: paymentProviderPlanId,
|
||||
});
|
||||
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/*
|
||||
* This API lists all the projects where user is its team member.
|
||||
* This API is usually used to show project selector dropdown in the UI
|
||||
|
||||
@@ -276,6 +276,142 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
},
|
||||
);
|
||||
|
||||
// embedded overall status badge api
|
||||
this.router.get(
|
||||
`${new this.entityType()
|
||||
.getCrudApiPath()
|
||||
?.toString()}/badge/:statusPageId`,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
try {
|
||||
const statusPageId: ObjectID = new ObjectID(
|
||||
req.params["statusPageId"] as string,
|
||||
);
|
||||
|
||||
const token: string = req.query["token"] as string;
|
||||
|
||||
if (!token) {
|
||||
return res.status(400).send("Token is required");
|
||||
}
|
||||
|
||||
// Fetch status page with security token
|
||||
const statusPage: StatusPage | null =
|
||||
await StatusPageService.findOneBy({
|
||||
query: {
|
||||
_id: statusPageId,
|
||||
enableEmbeddedOverallStatus: true,
|
||||
embeddedOverallStatusToken: token,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
projectId: true,
|
||||
downtimeMonitorStatuses: {
|
||||
_id: true,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!statusPage) {
|
||||
return res.status(404).send("Status badge not found or disabled");
|
||||
}
|
||||
|
||||
// Get status page resources and current statuses
|
||||
const statusPageResources: Array<StatusPageResource> =
|
||||
await StatusPageResourceService.findBy({
|
||||
query: {
|
||||
statusPageId: statusPageId,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
monitor: {
|
||||
_id: true,
|
||||
currentMonitorStatusId: true,
|
||||
},
|
||||
monitorGroupId: true,
|
||||
},
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Get monitor statuses
|
||||
const monitorStatuses: Array<MonitorStatus> =
|
||||
await MonitorStatusService.findBy({
|
||||
query: {
|
||||
projectId: statusPage.projectId!,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
name: true,
|
||||
color: true,
|
||||
priority: true,
|
||||
isOperationalState: true,
|
||||
},
|
||||
sort: {
|
||||
priority: SortOrder.Ascending,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Get monitor group current statuses
|
||||
const monitorGroupCurrentStatuses: Dictionary<ObjectID> =
|
||||
await StatusPageService.getMonitorGroupCurrentStatuses({
|
||||
statusPageResources,
|
||||
monitorStatuses,
|
||||
});
|
||||
|
||||
// Calculate overall status
|
||||
const overallStatus: MonitorStatus | null =
|
||||
StatusPageService.getOverallMonitorStatus({
|
||||
statusPageResources,
|
||||
monitorStatuses,
|
||||
monitorGroupCurrentStatuses,
|
||||
});
|
||||
|
||||
// Generate SVG badge
|
||||
const statusName: string = overallStatus?.name || "Unknown";
|
||||
const statusColor: string =
|
||||
overallStatus?.color?.toString() || "#808080";
|
||||
|
||||
const svg: string = `<svg xmlns="http://www.w3.org/2000/svg" width="150" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="a">
|
||||
<rect width="150" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#a)">
|
||||
<path fill="#555" d="M0 0h50v20H0z"/>
|
||||
<path fill="${statusColor}" d="M50 0h100v20H50z"/>
|
||||
<path fill="url(#b)" d="M0 0h150v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="25" y="15" fill="#010101" fill-opacity=".3">status</text>
|
||||
<text x="25" y="14">status</text>
|
||||
<text x="100" y="15" fill="#010101" fill-opacity=".3">${statusName}</text>
|
||||
<text x="100" y="14">${statusName}</text>
|
||||
</g>
|
||||
</svg>`;
|
||||
|
||||
res.setHeader("Content-Type", "image/svg+xml");
|
||||
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
return res.send(svg);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return res.status(500).send("Internal Server Error");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// confirm subscription api
|
||||
this.router.get(
|
||||
`${new this.entityType()
|
||||
@@ -673,6 +809,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
await this.checkHasReadAccess({
|
||||
statusPageId: statusPageId,
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
|
||||
const resources: Array<StatusPageResource> =
|
||||
@@ -733,6 +870,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
await this.checkHasReadAccess({
|
||||
statusPageId: statusPageId,
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
|
||||
/*
|
||||
@@ -1025,6 +1163,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
await this.checkHasReadAccess({
|
||||
statusPageId: statusPageId,
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
|
||||
const startDate: Date = OneUptimeDate.getSomeDaysAgo(90);
|
||||
@@ -1392,11 +1531,11 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
});
|
||||
|
||||
const overallStatus: MonitorStatus | null =
|
||||
this.getOverallMonitorStatus(
|
||||
StatusPageService.getOverallMonitorStatus({
|
||||
statusPageResources,
|
||||
monitorStatuses,
|
||||
monitorGroupCurrentStatuses,
|
||||
);
|
||||
});
|
||||
|
||||
const response: JSONObject = {
|
||||
overallStatus: overallStatus
|
||||
@@ -1472,7 +1611,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
await this.subscribeToStatusPage(req);
|
||||
await this.subscribeToStatusPage(req, res);
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
@@ -1509,7 +1648,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
await this.subscribeToStatusPage(req);
|
||||
await this.subscribeToStatusPage(req, res);
|
||||
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
@@ -1525,7 +1664,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
await this.manageExistingSubscription(req);
|
||||
await this.manageExistingSubscription(req, res);
|
||||
|
||||
return Response.sendEmptySuccessResponse(req, res);
|
||||
} catch (err) {
|
||||
@@ -1549,6 +1688,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
objectId,
|
||||
null,
|
||||
req,
|
||||
res,
|
||||
);
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, response);
|
||||
@@ -1572,8 +1712,8 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
const response: JSONObject = await this.getScheduledMaintenanceEvents(
|
||||
objectId,
|
||||
null,
|
||||
|
||||
req,
|
||||
res,
|
||||
);
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, response);
|
||||
@@ -1597,8 +1737,8 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
const response: JSONObject = await this.getAnnouncements(
|
||||
objectId,
|
||||
null,
|
||||
|
||||
req,
|
||||
res,
|
||||
);
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, response);
|
||||
@@ -1627,6 +1767,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
objectId,
|
||||
incidentId,
|
||||
req,
|
||||
res,
|
||||
);
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, response);
|
||||
@@ -1654,8 +1795,8 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
const response: JSONObject = await this.getScheduledMaintenanceEvents(
|
||||
objectId,
|
||||
scheduledMaintenanceId,
|
||||
|
||||
req,
|
||||
res,
|
||||
);
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, response);
|
||||
@@ -1683,8 +1824,8 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
const response: JSONObject = await this.getAnnouncements(
|
||||
objectId,
|
||||
announcementId,
|
||||
|
||||
req,
|
||||
res,
|
||||
);
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, response);
|
||||
@@ -1700,10 +1841,12 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
statusPageId: ObjectID,
|
||||
scheduledMaintenanceId: ObjectID | null,
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<JSONObject> {
|
||||
await this.checkHasReadAccess({
|
||||
statusPageId: statusPageId,
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
|
||||
const statusPage: StatusPage | null = await StatusPageService.findOneBy({
|
||||
@@ -2017,10 +2160,12 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
statusPageId: ObjectID,
|
||||
announcementId: ObjectID | null,
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<JSONObject> {
|
||||
await this.checkHasReadAccess({
|
||||
statusPageId: statusPageId,
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
|
||||
const statusPage: StatusPage | null = await StatusPageService.findOneBy({
|
||||
@@ -2192,7 +2337,10 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async manageExistingSubscription(req: ExpressRequest): Promise<void> {
|
||||
public async manageExistingSubscription(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
const statusPageId: ObjectID = new ObjectID(
|
||||
req.params["statusPageId"] as string,
|
||||
);
|
||||
@@ -2204,6 +2352,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
await this.checkHasReadAccess({
|
||||
statusPageId: statusPageId,
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
|
||||
const statusPage: StatusPage | null = await StatusPageService.findOneBy({
|
||||
@@ -2467,7 +2616,10 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async subscribeToStatusPage(req: ExpressRequest): Promise<void> {
|
||||
public async subscribeToStatusPage(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> {
|
||||
const objectId: ObjectID = new ObjectID(
|
||||
req.params["statusPageId"] as string,
|
||||
);
|
||||
@@ -2477,6 +2629,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
await this.checkHasReadAccess({
|
||||
statusPageId: objectId,
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
|
||||
const statusPage: StatusPage | null = await StatusPageService.findOneBy({
|
||||
@@ -2844,10 +2997,12 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
statusPageId: ObjectID,
|
||||
incidentId: ObjectID | null,
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<JSONObject> {
|
||||
await this.checkHasReadAccess({
|
||||
statusPageId: statusPageId,
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
|
||||
const statusPage: StatusPage | null = await StatusPageService.findOneBy({
|
||||
@@ -3099,56 +3254,6 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
return response;
|
||||
}
|
||||
|
||||
public getOverallMonitorStatus(
|
||||
statusPageResources: Array<StatusPageResource>,
|
||||
monitorStatuses: Array<MonitorStatus>,
|
||||
monitorGroupCurrentStatuses: Dictionary<ObjectID>,
|
||||
): MonitorStatus | null {
|
||||
let currentStatus: MonitorStatus | null =
|
||||
monitorStatuses.length > 0 && monitorStatuses[0]
|
||||
? monitorStatuses[0]
|
||||
: null;
|
||||
|
||||
const dict: Dictionary<number> = {};
|
||||
|
||||
for (const resource of statusPageResources) {
|
||||
if (resource.monitor?.currentMonitorStatusId) {
|
||||
if (
|
||||
!Object.keys(dict).includes(
|
||||
resource.monitor?.currentMonitorStatusId.toString() || "",
|
||||
)
|
||||
) {
|
||||
dict[resource.monitor?.currentMonitorStatusId?.toString()] = 1;
|
||||
} else {
|
||||
dict[resource.monitor!.currentMonitorStatusId!.toString()]!++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check status of monitor groups.
|
||||
|
||||
for (const groupId in monitorGroupCurrentStatuses) {
|
||||
const statusId: ObjectID | undefined =
|
||||
monitorGroupCurrentStatuses[groupId];
|
||||
|
||||
if (statusId) {
|
||||
if (!Object.keys(dict).includes(statusId.toString() || "")) {
|
||||
dict[statusId.toString()] = 1;
|
||||
} else {
|
||||
dict[statusId.toString()]!++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const monitorStatus of monitorStatuses) {
|
||||
if (monitorStatus._id && dict[monitorStatus._id]) {
|
||||
currentStatus = monitorStatus;
|
||||
}
|
||||
}
|
||||
|
||||
return currentStatus;
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async getStatusPageResourcesAndTimelines(data: {
|
||||
statusPageId: ObjectID;
|
||||
@@ -3406,6 +3511,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
public async checkHasReadAccess(data: {
|
||||
statusPageId: ObjectID;
|
||||
req: ExpressRequest;
|
||||
res: ExpressResponse;
|
||||
}): Promise<void> {
|
||||
const accessResult: {
|
||||
hasReadAccess: boolean;
|
||||
@@ -3413,6 +3519,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
} = await this.service.hasReadAccess({
|
||||
statusPageId: data.statusPageId,
|
||||
req: data.req,
|
||||
res: data.res,
|
||||
});
|
||||
|
||||
if (!accessResult.hasReadAccess) {
|
||||
|
||||
@@ -236,12 +236,16 @@ export default class UserNotificationLogTimelineAPI extends BaseAPI<
|
||||
if (timelineItem.isAcknowledged) {
|
||||
// already acknowledged. Then show already acknowledged page with view details button.
|
||||
|
||||
const viewDetailsRoute: Route = new Route(
|
||||
DashboardRoute.toString(),
|
||||
).addRoute(
|
||||
`/${timelineItem.projectId?.toString()}/${timelineItem.triggeredByIncidentId ? "incidents" : "alerts"}/${timelineItem.triggeredByIncidentId ? timelineItem.triggeredByIncidentId!.toString() : timelineItem.triggeredByAlertId!.toString()}`,
|
||||
);
|
||||
|
||||
const viewDetailsUrl: URL = new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
DashboardRoute.addRoute(
|
||||
`/${timelineItem.projectId?.toString()}/${timelineItem.triggeredByIncidentId ? "incidents" : "alerts"}/${timelineItem.triggeredByIncidentId ? timelineItem.triggeredByIncidentId!.toString() : timelineItem.triggeredByAlertId!.toString()}`,
|
||||
),
|
||||
viewDetailsRoute,
|
||||
);
|
||||
|
||||
return Response.render(
|
||||
@@ -273,30 +277,30 @@ export default class UserNotificationLogTimelineAPI extends BaseAPI<
|
||||
// redirect to dashboard to incidents page.
|
||||
|
||||
if (timelineItem.triggeredByIncidentId) {
|
||||
const incidentRoute: Route = new Route(
|
||||
DashboardRoute.toString(),
|
||||
).addRoute(
|
||||
`/${timelineItem.projectId?.toString()}/incidents/${timelineItem.triggeredByIncidentId!.toString()}`,
|
||||
);
|
||||
|
||||
return Response.redirect(
|
||||
req,
|
||||
res,
|
||||
new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
DashboardRoute.addRoute(
|
||||
`/${timelineItem.projectId?.toString()}/incidents/${timelineItem.triggeredByIncidentId!.toString()}`,
|
||||
),
|
||||
),
|
||||
new URL(httpProtocol, host, incidentRoute),
|
||||
);
|
||||
}
|
||||
|
||||
if (timelineItem.triggeredByAlertId) {
|
||||
const alertRoute: Route = new Route(
|
||||
DashboardRoute.toString(),
|
||||
).addRoute(
|
||||
`/${timelineItem.projectId?.toString()}/alerts/${timelineItem.triggeredByAlertId!.toString()}`,
|
||||
);
|
||||
|
||||
return Response.redirect(
|
||||
req,
|
||||
res,
|
||||
new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
DashboardRoute.addRoute(
|
||||
`/${timelineItem.projectId?.toString()}/alerts/${timelineItem.triggeredByAlertId!.toString()}`,
|
||||
),
|
||||
),
|
||||
new URL(httpProtocol, host, alertRoute),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { AccountsRoute, DashboardRoute } from "../ServiceRoute";
|
||||
import Hostname from "../Types/API/Hostname";
|
||||
import Protocol from "../Types/API/Protocol";
|
||||
import URL from "../Types/API/URL";
|
||||
import Route from "../Types/API/Route";
|
||||
import BadDataException from "../Types/Exception/BadDataException";
|
||||
import { JSONValue } from "../Types/JSON";
|
||||
import GlobalConfig from "../Models/DatabaseModels/GlobalConfig";
|
||||
@@ -56,7 +57,11 @@ export default class DatabaseConfig {
|
||||
@CaptureSpan()
|
||||
public static async getAccountsUrl(): Promise<URL> {
|
||||
const host: Hostname = await DatabaseConfig.getHost();
|
||||
return new URL(await DatabaseConfig.getHttpProtocol(), host, AccountsRoute);
|
||||
return new URL(
|
||||
await DatabaseConfig.getHttpProtocol(),
|
||||
host,
|
||||
new Route(AccountsRoute.toString()),
|
||||
);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -65,7 +70,7 @@ export default class DatabaseConfig {
|
||||
return new URL(
|
||||
await DatabaseConfig.getHttpProtocol(),
|
||||
host,
|
||||
DashboardRoute,
|
||||
new Route(DashboardRoute.toString()),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import BillingConfig from "./BillingConfig";
|
||||
import Protocol from "../Types/API/Protocol";
|
||||
import URL from "../Types/API/URL";
|
||||
import Route from "../Types/API/Route";
|
||||
import SubscriptionPlan from "../Types/Billing/SubscriptionPlan";
|
||||
import Email from "../Types/Email";
|
||||
import { JSONObject } from "../Types/JSON";
|
||||
@@ -22,6 +23,77 @@ export const getAllEnvVars: () => JSONObject = (): JSONObject => {
|
||||
return process.env;
|
||||
};
|
||||
|
||||
const FRONTEND_ENV_ALLOW_LIST: Array<string> = [
|
||||
"NODE_ENV",
|
||||
"HTTP_PROTOCOL",
|
||||
"HOST",
|
||||
"BILLING_ENABLED",
|
||||
"BILLING_PUBLIC_KEY",
|
||||
"IS_ENTERPRISE_EDITION",
|
||||
"STRIPE_PUBLIC_KEY",
|
||||
"VAPID_PUBLIC_KEY",
|
||||
"VAPID_SUBJECT",
|
||||
"VERSION",
|
||||
"STATUS_PAGE_CNAME_RECORD",
|
||||
"ANALYTICS_KEY",
|
||||
"ANALYTICS_HOST",
|
||||
"GIT_SHA",
|
||||
"APP_VERSION",
|
||||
"OPENTELEMETRY_EXPORTER_OTLP_ENDPOINT",
|
||||
"OPENTELEMETRY_EXPORTER_OTLP_HEADERS",
|
||||
"DISABLE_TELEMETRY",
|
||||
"SLACK_APP_CLIENT_ID",
|
||||
"MICROSOFT_TEAMS_APP_CLIENT_ID",
|
||||
];
|
||||
|
||||
const FRONTEND_ENV_ALLOW_PREFIXES: Array<string> = [
|
||||
"SUBSCRIPTION_PLAN_",
|
||||
"PUBLIC_",
|
||||
];
|
||||
|
||||
export const getFrontendEnvVars: () => JSONObject = (): JSONObject => {
|
||||
const frontendEnv: JSONObject = {};
|
||||
|
||||
for (const key of Object.keys(process.env)) {
|
||||
const shouldInclude: boolean =
|
||||
FRONTEND_ENV_ALLOW_LIST.includes(key) ||
|
||||
FRONTEND_ENV_ALLOW_PREFIXES.some((prefix: string) => {
|
||||
return key.startsWith(prefix);
|
||||
});
|
||||
|
||||
if (!shouldInclude) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value: string | undefined = process.env[key];
|
||||
|
||||
if (typeof value !== "undefined") {
|
||||
frontendEnv[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return frontendEnv;
|
||||
};
|
||||
|
||||
const parsePositiveNumberFromEnv: (
|
||||
envKey: string,
|
||||
fallback: number,
|
||||
) => number = (envKey: string, fallback: number): number => {
|
||||
const rawValue: string | undefined = process.env[envKey];
|
||||
|
||||
if (!rawValue) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const parsedValue: number = parseFloat(rawValue);
|
||||
|
||||
if (!Number.isFinite(parsedValue) || parsedValue <= 0) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return parsedValue;
|
||||
};
|
||||
|
||||
export const IsBillingEnabled: boolean = BillingConfig.IsBillingEnabled;
|
||||
export const BillingPublicKey: string = BillingConfig.BillingPublicKey;
|
||||
export const BillingPrivateKey: string = BillingConfig.BillingPrivateKey;
|
||||
@@ -256,6 +328,8 @@ export const HttpProtocol: Protocol =
|
||||
|
||||
export const Host: string = process.env["HOST"] || "";
|
||||
|
||||
export const ProvisionSsl: boolean = process.env["PROVISION_SSL"] === "true";
|
||||
|
||||
export const WorkflowScriptTimeoutInMS: number = process.env[
|
||||
"WORKFLOW_SCRIPT_TIMEOUT_IN_MS"
|
||||
]
|
||||
@@ -303,36 +377,69 @@ export const NotificationSlackWebhookOnSubscriptionUpdate: string =
|
||||
export const AdminDashboardClientURL: URL = new URL(
|
||||
HttpProtocol,
|
||||
Host,
|
||||
AdminDashboardRoute,
|
||||
new Route(AdminDashboardRoute.toString()),
|
||||
);
|
||||
|
||||
export const AppApiClientUrl: URL = new URL(HttpProtocol, Host, AppApiRoute);
|
||||
export const AppApiClientUrl: URL = new URL(
|
||||
HttpProtocol,
|
||||
Host,
|
||||
new Route(AppApiRoute.toString()),
|
||||
);
|
||||
|
||||
export const StatusPageApiClientUrl: URL = new URL(
|
||||
HttpProtocol,
|
||||
Host,
|
||||
StatusPageApiRoute,
|
||||
new Route(StatusPageApiRoute.toString()),
|
||||
);
|
||||
|
||||
export const DashboardClientUrl: URL = new URL(
|
||||
HttpProtocol,
|
||||
Host,
|
||||
DashboardRoute,
|
||||
new Route(DashboardRoute.toString()),
|
||||
);
|
||||
|
||||
export const AccountsClientUrl: URL = new URL(
|
||||
HttpProtocol,
|
||||
Host,
|
||||
AccountsRoute,
|
||||
new Route(AccountsRoute.toString()),
|
||||
);
|
||||
|
||||
export const HomeClientUrl: URL = new URL(HttpProtocol, Host, HomeRoute);
|
||||
export const HomeClientUrl: URL = new URL(
|
||||
HttpProtocol,
|
||||
Host,
|
||||
new Route(HomeRoute.toString()),
|
||||
);
|
||||
|
||||
export const DocsClientUrl: URL = new URL(HttpProtocol, Host, DocsRoute);
|
||||
export const DocsClientUrl: URL = new URL(
|
||||
HttpProtocol,
|
||||
Host,
|
||||
new Route(DocsRoute.toString()),
|
||||
);
|
||||
|
||||
export const DisableTelemetry: boolean =
|
||||
process.env["DISABLE_TELEMETRY"] === "true";
|
||||
|
||||
export const IsEnterpriseEdition: boolean =
|
||||
process.env["IS_ENTERPRISE_EDITION"] === "true";
|
||||
|
||||
export const AverageSpanRowSizeInBytes: number = parsePositiveNumberFromEnv(
|
||||
"AVERAGE_SPAN_ROW_SIZE_IN_BYTES",
|
||||
1024,
|
||||
);
|
||||
|
||||
export const AverageLogRowSizeInBytes: number = parsePositiveNumberFromEnv(
|
||||
"AVERAGE_LOG_ROW_SIZE_IN_BYTES",
|
||||
1024,
|
||||
);
|
||||
|
||||
export const AverageMetricRowSizeInBytes: number = parsePositiveNumberFromEnv(
|
||||
"AVERAGE_METRIC_ROW_SIZE_IN_BYTES",
|
||||
1024,
|
||||
);
|
||||
|
||||
export const AverageExceptionRowSizeInBytes: number =
|
||||
parsePositiveNumberFromEnv("AVERAGE_EXCEPTION_ROW_SIZE_IN_BYTES", 1024);
|
||||
|
||||
export const SlackAppClientId: string | null =
|
||||
process.env["SLACK_APP_CLIENT_ID"] || null;
|
||||
export const SlackAppClientSecret: string | null =
|
||||
@@ -355,3 +462,7 @@ export const VapidPrivateKey: string | undefined =
|
||||
|
||||
export const VapidSubject: string =
|
||||
process.env["VAPID_SUBJECT"] || "mailto:support@oneuptime.com";
|
||||
|
||||
export const EnterpriseLicenseValidationUrl: URL = URL.fromString(
|
||||
"https://oneuptime.com/api/enterprise-license/validate",
|
||||
);
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -7,6 +7,10 @@ import { JSONArray, JSONObject } from "../../Types/JSON";
|
||||
import JSONFunctions from "../../Types/JSONFunctions";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
|
||||
type CacheSetOptions = {
|
||||
expiresInSeconds: number;
|
||||
};
|
||||
|
||||
export default abstract class GlobalCache {
|
||||
@CaptureSpan()
|
||||
public static async getJSONObject(
|
||||
@@ -56,8 +60,9 @@ export default abstract class GlobalCache {
|
||||
namespace: string,
|
||||
key: string,
|
||||
value: string[],
|
||||
options?: CacheSetOptions,
|
||||
): Promise<void> {
|
||||
await this.setString(namespace, key, JSON.stringify(value));
|
||||
await this.setString(namespace, key, JSON.stringify(value), options);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -136,11 +141,13 @@ export default abstract class GlobalCache {
|
||||
namespace: string,
|
||||
key: string,
|
||||
value: JSONObject,
|
||||
options?: CacheSetOptions,
|
||||
): Promise<void> {
|
||||
await this.setString(
|
||||
namespace,
|
||||
key,
|
||||
JSON.stringify(JSONFunctions.serialize(value)),
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -149,6 +156,7 @@ export default abstract class GlobalCache {
|
||||
namespace: string,
|
||||
key: string,
|
||||
value: string,
|
||||
options?: CacheSetOptions,
|
||||
): Promise<void> {
|
||||
const client: ClientType | null = Redis.getClient();
|
||||
|
||||
@@ -157,9 +165,8 @@ export default abstract class GlobalCache {
|
||||
}
|
||||
|
||||
await client.set(`${namespace}-${key}`, value);
|
||||
await client.expire(
|
||||
`${namespace}-${key}`,
|
||||
OneUptimeDate.getSecondsInDays(30),
|
||||
);
|
||||
const expiresInSeconds: number =
|
||||
options?.expiresInSeconds ?? OneUptimeDate.getSecondsInDays(30);
|
||||
await client.expire(`${namespace}-${key}`, expiresInSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1760345757975 implements MigrationInterface {
|
||||
public name = "MigrationName1760345757975";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "WhatsAppLog" ADD "whatsAppMessageId" character varying(100)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_2090742b9abffadde19dab2026" ON "WhatsAppLog" ("whatsAppMessageId") `,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "public"."IDX_2090742b9abffadde19dab2026"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "WhatsAppLog" DROP COLUMN "whatsAppMessageId"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1760357680881 implements MigrationInterface {
|
||||
public name = "MigrationName1760357680881";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD "metaWhatsAppWebhookVerifyToken" character varying(100)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD CONSTRAINT "UQ_afe98d53b718f485d3d64b383b8" UNIQUE ("metaWhatsAppWebhookVerifyToken")`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP CONSTRAINT "UQ_afe98d53b718f485d3d64b383b8"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP COLUMN "metaWhatsAppWebhookVerifyToken"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1761232578396 implements MigrationInterface {
|
||||
public name = "MigrationName1761232578396";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" ADD "enableEmbeddedOverallStatus" boolean NOT NULL DEFAULT false`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" ADD "embeddedOverallStatusToken" character varying(100)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_350d2250fb17e0dc10663de72a" ON "StatusPage" ("embeddedOverallStatusToken") `,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "public"."IDX_350d2250fb17e0dc10663de72a"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" DROP COLUMN "embeddedOverallStatusToken"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" DROP COLUMN "enableEmbeddedOverallStatus"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1761834523183 implements MigrationInterface {
|
||||
public name = "MigrationName1761834523183";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "IncidentPostmortemTemplate" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "postmortemNote" text NOT NULL, "templateName" character varying(100) NOT NULL, "templateDescription" character varying(500) NOT NULL, "createdByUserId" uuid, "deletedByUserId" uuid, CONSTRAINT "PK_76a09ebf10e7874f0c8ee1f0120" PRIMARY KEY ("_id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_2a18729813c7c666cc37683c4e" ON "IncidentPostmortemTemplate" ("projectId") `,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_c791fe4d7179b57064ace561c3" ON "IncidentPostmortemTemplate" ("postmortemNote") `,
|
||||
);
|
||||
await queryRunner.query(`ALTER TABLE "Incident" ADD "postmortemNote" text`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "IncidentPostmortemTemplate" ADD CONSTRAINT "FK_2a18729813c7c666cc37683c4ea" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "IncidentPostmortemTemplate" ADD CONSTRAINT "FK_961ac93c4d7ea881170692333d0" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "IncidentPostmortemTemplate" ADD CONSTRAINT "FK_2e886387888f1311f361d569b8e" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "IncidentPostmortemTemplate" DROP CONSTRAINT "FK_2e886387888f1311f361d569b8e"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "IncidentPostmortemTemplate" DROP CONSTRAINT "FK_961ac93c4d7ea881170692333d0"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "IncidentPostmortemTemplate" DROP CONSTRAINT "FK_2a18729813c7c666cc37683c4ea"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "Incident" DROP COLUMN "postmortemNote"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "public"."IDX_c791fe4d7179b57064ace561c3"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "public"."IDX_2a18729813c7c666cc37683c4e"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "IncidentPostmortemTemplate"`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1762181014879 implements MigrationInterface {
|
||||
public name = "MigrationName1762181014879";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "EnterpriseLicense" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "companyName" character varying(100) NOT NULL, "licenseKey" character varying(100) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, "annualContractValue" integer, CONSTRAINT "UQ_d35e76999092d8a16a66e84c17c" UNIQUE ("licenseKey"), CONSTRAINT "PK_731aa4437672f250fd51ec04166" PRIMARY KEY ("_id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE UNIQUE INDEX "IDX_d35e76999092d8a16a66e84c17" ON "EnterpriseLicense" ("licenseKey") `,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD "enterpriseCompanyName" character varying(100)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD CONSTRAINT "UQ_46983cb1a59503dc09fc84bbe0c" UNIQUE ("enterpriseCompanyName")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD "enterpriseLicenseKey" character varying(100)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD CONSTRAINT "UQ_89f80e8a18c3372ee150a3812c1" UNIQUE ("enterpriseLicenseKey")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD "enterpriseLicenseExpiresAt" TIMESTAMP WITH TIME ZONE`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD CONSTRAINT "UQ_a361278e9ce4056d59e8fb13319" UNIQUE ("enterpriseLicenseExpiresAt")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD "enterpriseLicenseToken" text`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" ADD CONSTRAINT "UQ_b0b9322c111c0cc629fedbb4eb3" UNIQUE ("enterpriseLicenseToken")`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP CONSTRAINT "UQ_b0b9322c111c0cc629fedbb4eb3"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP COLUMN "enterpriseLicenseToken"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP CONSTRAINT "UQ_a361278e9ce4056d59e8fb13319"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP COLUMN "enterpriseLicenseExpiresAt"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP CONSTRAINT "UQ_89f80e8a18c3372ee150a3812c1"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP COLUMN "enterpriseLicenseKey"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP CONSTRAINT "UQ_46983cb1a59503dc09fc84bbe0c"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "GlobalConfig" DROP COLUMN "enterpriseCompanyName"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "public"."IDX_d35e76999092d8a16a66e84c17"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "EnterpriseLicense"`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1762430566091 implements MigrationInterface {
|
||||
public name = 'MigrationName1762430566091'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "StatusPagePrivateUser" ADD "jwtRefreshToken" character varying(100)`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "StatusPagePrivateUser" DROP COLUMN "jwtRefreshToken"`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -175,6 +175,12 @@ import { MigrationName1759175457008 } from "./1759175457008-MigrationName";
|
||||
import { MigrationName1759232954703 } from "./1759232954703-MigrationName";
|
||||
import { RenameUserTwoFactorAuthToUserTotpAuth1759234532998 } from "./1759234532998-MigrationName";
|
||||
import { MigrationName1759943124812 } from "./1759943124812-MigrationName";
|
||||
import { MigrationName1760345757975 } from "./1760345757975-MigrationName";
|
||||
import { MigrationName1760357680881 } from "./1760357680881-MigrationName";
|
||||
import { MigrationName1761232578396 } from "./1761232578396-MigrationName";
|
||||
import { MigrationName1761834523183 } from "./1761834523183-MigrationName";
|
||||
import { MigrationName1762181014879 } from "./1762181014879-MigrationName";
|
||||
import { MigrationName1762430566091 } from "./1762430566091-MigrationName";
|
||||
|
||||
export default [
|
||||
InitialMigration,
|
||||
@@ -354,4 +360,10 @@ export default [
|
||||
MigrationName1759232954703,
|
||||
RenameUserTwoFactorAuthToUserTotpAuth1759234532998,
|
||||
MigrationName1759943124812,
|
||||
MigrationName1760345757975,
|
||||
MigrationName1760357680881,
|
||||
MigrationName1761232578396,
|
||||
MigrationName1761834523183,
|
||||
MigrationName1762181014879,
|
||||
MigrationName1762430566091
|
||||
];
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
ClusterKey,
|
||||
RedisHostname,
|
||||
RedisPassword,
|
||||
RedisPort,
|
||||
} from "../EnvironmentConfig";
|
||||
import { ClusterKey } from "../EnvironmentConfig";
|
||||
import Dictionary from "../../Types/Dictionary";
|
||||
import { JSONObject } from "../../Types/JSON";
|
||||
import { Queue as BullQueue, Job, JobsOptions } from "bullmq";
|
||||
@@ -13,6 +8,7 @@ import { BullMQAdapter } from "@bull-board/api/bullMQAdapter";
|
||||
import { ExpressRouter } from "../Utils/Express";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
import logger from "../Utils/Logger";
|
||||
import Redis from "./Redis";
|
||||
|
||||
export enum QueueName {
|
||||
Workflow = "Workflow",
|
||||
@@ -25,6 +21,7 @@ export enum QueueName {
|
||||
}
|
||||
|
||||
export type QueueJob = Job;
|
||||
type BullBoardQueues = Parameters<typeof createBullBoard>[0]["queues"];
|
||||
|
||||
export default class Queue {
|
||||
private static queueDict: Dictionary<BullQueue> = {};
|
||||
@@ -39,6 +36,11 @@ export default class Queue {
|
||||
}>
|
||||
> = {};
|
||||
|
||||
// BullMQ rejects custom IDs containing colons, so normalize them early.
|
||||
private static sanitizeJobId(jobId: string): string {
|
||||
return jobId.replace(/:/g, "-");
|
||||
}
|
||||
|
||||
private static async setupReconnectListener(
|
||||
queue: BullQueue,
|
||||
queueName: QueueName,
|
||||
@@ -82,11 +84,7 @@ export default class Queue {
|
||||
}
|
||||
|
||||
const queue: BullQueue = new BullQueue(queueName, {
|
||||
connection: {
|
||||
host: RedisHostname.toString(),
|
||||
port: RedisPort.toNumber(),
|
||||
password: RedisPassword,
|
||||
},
|
||||
connection: Redis.getRedisOptions(),
|
||||
// Keep BullMQ data under control to avoid Redis bloat
|
||||
defaultJobOptions: {
|
||||
// keep only recent completed/failed jobs
|
||||
@@ -138,14 +136,17 @@ export default class Queue {
|
||||
return;
|
||||
}
|
||||
|
||||
const job: Job | undefined = await this.getQueue(queueName).getJob(jobId);
|
||||
const sanitizedJobId: string = this.sanitizeJobId(jobId.toString());
|
||||
|
||||
const job: Job | undefined =
|
||||
await this.getQueue(queueName).getJob(sanitizedJobId);
|
||||
|
||||
if (job) {
|
||||
await job.remove();
|
||||
}
|
||||
|
||||
// remove existing repeatable job
|
||||
await this.getQueue(queueName).removeRepeatableByKey(jobId);
|
||||
await this.getQueue(queueName).removeRepeatableByKey(sanitizedJobId);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -157,12 +158,15 @@ export default class Queue {
|
||||
public static getQueueInspectorRouter(): ExpressRouter {
|
||||
const serverAdapter: ExpressAdapter = new ExpressAdapter();
|
||||
|
||||
const queueAdapters: BullMQAdapter[] = Object.values(QueueName).map(
|
||||
(queueName: QueueName) => {
|
||||
return new BullMQAdapter(this.getQueue(queueName));
|
||||
},
|
||||
);
|
||||
|
||||
createBullBoard({
|
||||
queues: [
|
||||
...Object.values(QueueName).map((queueName: QueueName) => {
|
||||
return new BullMQAdapter(this.getQueue(queueName));
|
||||
}),
|
||||
],
|
||||
// Cast keeps compatibility until bull-board widens QueueJob.progress
|
||||
queues: queueAdapters as unknown as BullBoardQueues,
|
||||
serverAdapter: serverAdapter,
|
||||
});
|
||||
|
||||
@@ -187,8 +191,10 @@ export default class Queue {
|
||||
repeatableKey?: string | undefined;
|
||||
},
|
||||
): Promise<Job> {
|
||||
const sanitizedJobId: string = this.sanitizeJobId(jobId.toString());
|
||||
|
||||
const optionsObject: JobsOptions = {
|
||||
jobId: jobId.toString(),
|
||||
jobId: sanitizedJobId,
|
||||
};
|
||||
|
||||
if (options && options.scheduleAt) {
|
||||
@@ -197,7 +203,8 @@ export default class Queue {
|
||||
};
|
||||
}
|
||||
|
||||
const job: Job | undefined = await this.getQueue(queueName).getJob(jobId);
|
||||
const job: Job | undefined =
|
||||
await this.getQueue(queueName).getJob(sanitizedJobId);
|
||||
|
||||
if (job) {
|
||||
await job.remove();
|
||||
@@ -215,7 +222,7 @@ export default class Queue {
|
||||
if (!this.repeatableJobs[queueName]) {
|
||||
this.repeatableJobs[queueName] = {};
|
||||
}
|
||||
this.repeatableJobs[queueName]![jobId] = {
|
||||
this.repeatableJobs[queueName]![sanitizedJobId] = {
|
||||
jobName,
|
||||
data,
|
||||
options: optionsObject,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { RedisHostname, RedisPassword, RedisPort } from "../EnvironmentConfig";
|
||||
import { QueueJob, QueueName } from "./Queue";
|
||||
import TimeoutException from "../../Types/Exception/TimeoutException";
|
||||
import {
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
} from "../../Types/FunctionTypes";
|
||||
import { Worker } from "bullmq";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
import Redis from "./Redis";
|
||||
|
||||
export default class QueueWorker {
|
||||
@CaptureSpan()
|
||||
@@ -30,11 +30,7 @@ export default class QueueWorker {
|
||||
},
|
||||
): Worker {
|
||||
const worker: Worker = new Worker(queueName, onJobInQueue, {
|
||||
connection: {
|
||||
host: RedisHostname.toString(),
|
||||
port: RedisPort.toNumber(),
|
||||
password: RedisPassword,
|
||||
},
|
||||
connection: Redis.getRedisOptions(),
|
||||
concurrency: options.concurrency,
|
||||
// Only set these values if provided so we do not override BullMQ defaults
|
||||
...(options.lockDuration ? { lockDuration: options.lockDuration } : {}),
|
||||
|
||||
@@ -10,7 +10,9 @@ import {
|
||||
OneUptimeRequest,
|
||||
} from "../Utils/Express";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
import JSONWebToken from "../Utils/JsonWebToken";
|
||||
import JSONWebToken, {
|
||||
RefreshTokenData,
|
||||
} from "../Utils/JsonWebToken";
|
||||
import logger from "../Utils/Logger";
|
||||
import Response from "../Utils/Response";
|
||||
import ProjectMiddleware from "./ProjectAuthorization";
|
||||
@@ -33,6 +35,8 @@ import {
|
||||
import UserType from "../../Types/UserType";
|
||||
import Project from "../../Models/DatabaseModels/Project";
|
||||
import UserPermissionUtil from "../Utils/UserPermission/UserPermission";
|
||||
import User from "../../Models/DatabaseModels/User";
|
||||
import { EncryptionSecret } from "../EnvironmentConfig";
|
||||
|
||||
export default class UserMiddleware {
|
||||
/*
|
||||
@@ -161,22 +165,44 @@ export default class UserMiddleware {
|
||||
);
|
||||
}
|
||||
|
||||
const accessToken: string | undefined =
|
||||
let accessToken: string | undefined =
|
||||
UserMiddleware.getAccessTokenFromExpressRequest(req);
|
||||
let userAuthorization: JSONWebTokenData | null = null;
|
||||
|
||||
if (!accessToken) {
|
||||
if (accessToken) {
|
||||
try {
|
||||
userAuthorization = JSONWebToken.decode(accessToken);
|
||||
} catch (err) {
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Invalid access token, attempting refresh: ${
|
||||
error.message || "unknown error"
|
||||
}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!userAuthorization) {
|
||||
const refreshedSession:
|
||||
| {
|
||||
accessToken: string;
|
||||
userAuthorization: JSONWebTokenData;
|
||||
}
|
||||
| null = await UserMiddleware.tryRefreshSession(req, res);
|
||||
|
||||
if (refreshedSession) {
|
||||
accessToken = refreshedSession.accessToken;
|
||||
userAuthorization = refreshedSession.userAuthorization;
|
||||
}
|
||||
}
|
||||
|
||||
if (!userAuthorization) {
|
||||
oneuptimeRequest.userType = UserType.Public;
|
||||
return next();
|
||||
}
|
||||
|
||||
try {
|
||||
oneuptimeRequest.userAuthorization = JSONWebToken.decode(accessToken);
|
||||
} catch (err) {
|
||||
// if the token is invalid or expired, it'll throw this error.
|
||||
logger.error(err);
|
||||
oneuptimeRequest.userType = UserType.Public;
|
||||
return next();
|
||||
}
|
||||
oneuptimeRequest.userAuthorization = userAuthorization;
|
||||
|
||||
if (oneuptimeRequest.userAuthorization.isMasterAdmin) {
|
||||
oneuptimeRequest.userType = UserType.MasterAdmin;
|
||||
@@ -184,7 +210,7 @@ export default class UserMiddleware {
|
||||
oneuptimeRequest.userType = UserType.User;
|
||||
}
|
||||
|
||||
const userId: string = oneuptimeRequest.userAuthorization.userId.toString();
|
||||
const userId: string = userAuthorization.userId.toString();
|
||||
|
||||
await UserService.updateOneBy({
|
||||
query: {
|
||||
@@ -290,6 +316,113 @@ export default class UserMiddleware {
|
||||
return next();
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
private static async tryRefreshSession(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<
|
||||
| {
|
||||
accessToken: string;
|
||||
userAuthorization: JSONWebTokenData;
|
||||
}
|
||||
| null
|
||||
> {
|
||||
const refreshToken: string | undefined =
|
||||
CookieUtil.getCookieFromExpressRequest(
|
||||
req,
|
||||
CookieUtil.getRefreshTokenKey(),
|
||||
);
|
||||
|
||||
if (!refreshToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let refreshTokenData: RefreshTokenData;
|
||||
|
||||
try {
|
||||
refreshTokenData = JSONWebToken.decodeRefreshToken(refreshToken);
|
||||
} catch (err) {
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Failed to decode refresh token during middleware refresh: ${
|
||||
error.message || "unknown error"
|
||||
}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
CookieUtil.removeCookie(res, CookieUtil.getRefreshTokenKey());
|
||||
CookieUtil.removeCookie(res, CookieUtil.getUserTokenKey());
|
||||
return null;
|
||||
}
|
||||
|
||||
const hashedSessionId: string = await HashedString.hashValue(
|
||||
refreshTokenData.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
const user: User | null = await UserService.findOneBy({
|
||||
query: {
|
||||
_id: refreshTokenData.userId,
|
||||
jwtRefreshToken: hashedSessionId,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
isMasterAdmin: true,
|
||||
profilePictureId: true,
|
||||
timezone: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
CookieUtil.removeCookie(res, CookieUtil.getRefreshTokenKey());
|
||||
CookieUtil.removeCookie(res, CookieUtil.getUserTokenKey());
|
||||
return null;
|
||||
}
|
||||
|
||||
const session = CookieUtil.setUserCookie({
|
||||
expressResponse: res,
|
||||
user: user,
|
||||
isGlobalLogin: refreshTokenData.isGlobalLogin,
|
||||
});
|
||||
|
||||
if (!req.cookies) {
|
||||
req.cookies = {} as Dictionary<string>;
|
||||
}
|
||||
|
||||
req.cookies[CookieUtil.getUserTokenKey()] = session.accessToken;
|
||||
req.cookies[CookieUtil.getRefreshTokenKey()] = session.refreshToken;
|
||||
|
||||
const hashedNewSessionId: string = await HashedString.hashValue(
|
||||
session.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
await UserService.updateOneBy({
|
||||
query: {
|
||||
_id: user.id!,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: hashedNewSessionId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const userAuthorization: JSONWebTokenData = JSONWebToken.decode(
|
||||
session.accessToken,
|
||||
);
|
||||
|
||||
return {
|
||||
accessToken: session.accessToken,
|
||||
userAuthorization,
|
||||
};
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public static async getUserTenantAccessPermissionWithTenantId(data: {
|
||||
req: ExpressRequest;
|
||||
|
||||
@@ -19,7 +19,7 @@ export class Service extends DatabaseService<Model> {
|
||||
super(Model);
|
||||
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import AlertState from "../../Models/DatabaseModels/AlertState";
|
||||
import AlertStateTimeline from "../../Models/DatabaseModels/AlertStateTimeline";
|
||||
import User from "../../Models/DatabaseModels/User";
|
||||
import { IsBillingEnabled } from "../EnvironmentConfig";
|
||||
import TelemetryType from "../../Types/Telemetry/TelemetryType";
|
||||
import logger from "../Utils/Logger";
|
||||
import TelemetryUtil from "../Utils/Telemetry/Telemetry";
|
||||
import MetricService from "./MetricService";
|
||||
@@ -60,7 +59,7 @@ export class Service extends DatabaseService<Model> {
|
||||
public constructor() {
|
||||
super(Model);
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1156,6 +1155,9 @@ ${alertSeverity.name}
|
||||
alertSeverityId: alert.alertSeverity?._id?.toString(),
|
||||
alertSeverityName: alert.alertSeverity?.name?.toString(),
|
||||
};
|
||||
alertCountMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
||||
alertCountMetric.attributes,
|
||||
);
|
||||
|
||||
alertCountMetric.time = alertStartsAt;
|
||||
alertCountMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
||||
@@ -1204,6 +1206,9 @@ ${alertSeverity.name}
|
||||
alertSeverityId: alert.alertSeverity?._id?.toString(),
|
||||
alertSeverityName: alert.alertSeverity?.name?.toString(),
|
||||
};
|
||||
timeToAcknowledgeMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
||||
timeToAcknowledgeMetric.attributes,
|
||||
);
|
||||
|
||||
timeToAcknowledgeMetric.time =
|
||||
ackAlertStateTimeline?.startsAt ||
|
||||
@@ -1257,6 +1262,9 @@ ${alertSeverity.name}
|
||||
alertSeverityId: alert.alertSeverity?._id?.toString(),
|
||||
alertSeverityName: alert.alertSeverity?.name?.toString(),
|
||||
};
|
||||
timeToResolveMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
||||
timeToResolveMetric.attributes,
|
||||
);
|
||||
|
||||
timeToResolveMetric.time =
|
||||
resolvedAlertStateTimeline?.startsAt ||
|
||||
@@ -1303,6 +1311,9 @@ ${alertSeverity.name}
|
||||
alertSeverityId: alert.alertSeverity?._id?.toString(),
|
||||
alertSeverityName: alert.alertSeverity?.name?.toString(),
|
||||
};
|
||||
alertDurationMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
||||
alertDurationMetric.attributes,
|
||||
);
|
||||
|
||||
alertDurationMetric.time =
|
||||
lastAlertStateTimeline?.startsAt ||
|
||||
@@ -1329,15 +1340,6 @@ ${alertSeverity.name}
|
||||
},
|
||||
});
|
||||
|
||||
// index attributes
|
||||
TelemetryUtil.indexAttributes({
|
||||
attributes: ["monitorId", "projectId", "alertId", "monitorName"],
|
||||
projectId: alert.projectId,
|
||||
telemetryType: TelemetryType.Metric,
|
||||
}).catch((err: Error) => {
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
TelemetryUtil.indexMetricNameServiceNameMap({
|
||||
metricNameServiceNameMap: metricTypesMap,
|
||||
projectId: alert.projectId,
|
||||
|
||||
@@ -29,7 +29,7 @@ export class Service extends DatabaseService<AlertStateTimeline> {
|
||||
public constructor() {
|
||||
super(AlertStateTimeline);
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
||||
import OneUptimeDate from "../../Types/Date";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import Exception from "../../Types/Exception/Exception";
|
||||
import ExceptionCode from "../../Types/Exception/ExceptionCode";
|
||||
import { JSONObject } from "../../Types/JSON";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import PositiveNumber from "../../Types/PositiveNumber";
|
||||
@@ -55,13 +56,18 @@ import Sort from "../Types/AnalyticsDatabase/Sort";
|
||||
import AggregatedModel from "../../Types/BaseDatabase/AggregatedModel";
|
||||
import ModelEventType from "../../Types/Realtime/ModelEventType";
|
||||
|
||||
export type Results = ResultSet<"JSON">;
|
||||
export type DbJSONResponse = ResponseJSON<{
|
||||
data?: Array<JSONObject>;
|
||||
}>;
|
||||
|
||||
export default class AnalyticsDatabaseService<
|
||||
TBaseModel extends AnalyticsBaseModel,
|
||||
> extends BaseService {
|
||||
public modelType!: { new (): TBaseModel };
|
||||
public database!: ClickhouseDatabase;
|
||||
public model!: TBaseModel;
|
||||
public databaseClient!: ClickhouseClient;
|
||||
public databaseClient!: ClickhouseClient | null;
|
||||
public statementGenerator!: StatementGenerator<TBaseModel>;
|
||||
|
||||
public constructor(data: {
|
||||
@@ -77,7 +83,7 @@ export default class AnalyticsDatabaseService<
|
||||
this.database = ClickhouseAppInstance; // default database
|
||||
}
|
||||
|
||||
this.databaseClient = this.database.getDataSource() as ClickhouseClient;
|
||||
this.databaseClient = this.database.getDataSource();
|
||||
|
||||
this.statementGenerator = new StatementGenerator<TBaseModel>({
|
||||
modelType: this.modelType,
|
||||
@@ -85,6 +91,46 @@ export default class AnalyticsDatabaseService<
|
||||
});
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async insertJsonRows(rows: Array<JSONObject>): Promise<void> {
|
||||
if (!rows || rows.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const client: ClickhouseClient = this.getDatabaseClient();
|
||||
|
||||
const tableName: string = this.model.tableName;
|
||||
|
||||
if (!tableName) {
|
||||
throw new Exception(
|
||||
ExceptionCode.BadDataException,
|
||||
"Analytics model table name not configured",
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await client.insert({
|
||||
table: tableName,
|
||||
values: rows,
|
||||
format: "JSONEachRow",
|
||||
clickhouse_settings: {
|
||||
async_insert: 1,
|
||||
wait_for_async_insert: 0,
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
`ClickHouse insert succeeded for table ${tableName} at ${OneUptimeDate.toString(OneUptimeDate.getCurrentDate())}`,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`ClickHouse insert failed for table ${tableName} at ${OneUptimeDate.toString(OneUptimeDate.getCurrentDate())}`,
|
||||
);
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async doesColumnExistInDatabase(columnName: string): Promise<boolean> {
|
||||
const statement: string =
|
||||
@@ -802,23 +848,21 @@ export default class AnalyticsDatabaseService<
|
||||
|
||||
public useDefaultDatabase(): void {
|
||||
this.database = ClickhouseAppInstance;
|
||||
this.databaseClient = this.database.getDataSource() as ClickhouseClient;
|
||||
this.databaseClient = this.database.getDataSource();
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async execute(
|
||||
statement: Statement | string
|
||||
): Promise<ExecResult<Stream>> {
|
||||
if (!this.databaseClient) {
|
||||
this.useDefaultDatabase();
|
||||
}
|
||||
const client: ClickhouseClient = this.getDatabaseClient();
|
||||
|
||||
const query: string =
|
||||
statement instanceof Statement ? statement.query : statement;
|
||||
const queryParams: Record<string, unknown> | undefined =
|
||||
statement instanceof Statement ? statement.query_params : undefined;
|
||||
|
||||
return (await this.databaseClient.exec({
|
||||
return (await client.exec({
|
||||
query: query,
|
||||
query_params: queryParams || (undefined as any), // undefined is not specified in the type for query_params, but its ok to pass undefined.
|
||||
})) as ExecResult<Stream>;
|
||||
@@ -828,22 +872,43 @@ export default class AnalyticsDatabaseService<
|
||||
public async executeQuery(
|
||||
statement: Statement | string
|
||||
): Promise<ResultSet<"JSON">> {
|
||||
if (!this.databaseClient) {
|
||||
this.useDefaultDatabase();
|
||||
}
|
||||
const client: ClickhouseClient = this.getDatabaseClient();
|
||||
|
||||
const query: string =
|
||||
statement instanceof Statement ? statement.query : statement;
|
||||
const queryParams: Record<string, unknown> | undefined =
|
||||
statement instanceof Statement ? statement.query_params : undefined;
|
||||
|
||||
return await this.databaseClient.query({
|
||||
return await client.query({
|
||||
query: query,
|
||||
format: "JSON",
|
||||
query_params: queryParams || (undefined as any), // undefined is not specified in the type for query_params, but its ok to pass undefined.
|
||||
});
|
||||
}
|
||||
|
||||
private getDatabaseClient(): ClickhouseClient {
|
||||
/*
|
||||
* Refresh the ClickHouse client lazily so services created before the
|
||||
* ClickHouse connection was established pick up the live client.
|
||||
*/
|
||||
if (!this.database) {
|
||||
this.useDefaultDatabase();
|
||||
}
|
||||
|
||||
if (!this.databaseClient && this.database) {
|
||||
this.databaseClient = this.database.getDataSource();
|
||||
}
|
||||
|
||||
if (!this.databaseClient) {
|
||||
throw new Exception(
|
||||
ExceptionCode.DatabaseNotConnectedException,
|
||||
"ClickHouse client is not connected",
|
||||
);
|
||||
}
|
||||
|
||||
return this.databaseClient;
|
||||
}
|
||||
|
||||
protected async onUpdateSuccess(
|
||||
onUpdate: OnUpdate<TBaseModel>,
|
||||
_updatedItemIds: Array<ObjectID>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { EncryptionSecret, WorkflowHostname } from "../EnvironmentConfig";
|
||||
import PostgresAppInstance from "../Infrastructure/PostgresDatabase";
|
||||
import ClusterKeyAuthorization from "../Middleware/ClusterKeyAuthorization";
|
||||
import CountBy from "../Types/Database/CountBy";
|
||||
import FindAllBy from "../Types/Database/FindAllBy";
|
||||
import CreateBy from "../Types/Database/CreateBy";
|
||||
import DeleteBy from "../Types/Database/DeleteBy";
|
||||
import DeleteById from "../Types/Database/DeleteById";
|
||||
@@ -1168,6 +1169,75 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async findAllBy(
|
||||
findAllBy: FindAllBy<TBaseModel>,
|
||||
): Promise<Array<TBaseModel>> {
|
||||
const { limit, skip, ...rest } = findAllBy;
|
||||
|
||||
let remaining: number | undefined = this.normalizePositiveNumber(limit);
|
||||
let currentSkip: number = this.normalizePositiveNumber(skip) || 0;
|
||||
|
||||
const results: Array<TBaseModel> = [];
|
||||
|
||||
while (true) {
|
||||
const currentBatchSize: number =
|
||||
remaining !== undefined
|
||||
? Math.min(LIMIT_MAX, Math.max(remaining, 0))
|
||||
: LIMIT_MAX;
|
||||
|
||||
if (currentBatchSize <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const page: Array<TBaseModel> = await this.findBy({
|
||||
...rest,
|
||||
skip: currentSkip,
|
||||
limit: currentBatchSize,
|
||||
});
|
||||
|
||||
if (page.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
results.push(...page);
|
||||
|
||||
currentSkip += page.length;
|
||||
|
||||
if (remaining !== undefined) {
|
||||
remaining -= page.length;
|
||||
|
||||
if (remaining <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (page.length < currentBatchSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private normalizePositiveNumber(
|
||||
value?: PositiveNumber | number,
|
||||
): number | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (value instanceof PositiveNumber) {
|
||||
return value.toNumber();
|
||||
}
|
||||
|
||||
if (typeof value === "number") {
|
||||
return value;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async findBy(findBy: FindBy<TBaseModel>): Promise<Array<TBaseModel>> {
|
||||
return await this._findBy(findBy);
|
||||
|
||||
@@ -7,6 +7,9 @@ import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import Text from "../../Types/Text";
|
||||
import Model from "../../Models/DatabaseModels/Domain";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import { FindWhere } from "../../Types/BaseDatabase/Query";
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor() {
|
||||
super(Model);
|
||||
@@ -32,6 +35,12 @@ export class Service extends DatabaseService<Model> {
|
||||
createBy.data.domain = new Domain(domain.trim().toLowerCase());
|
||||
}
|
||||
|
||||
if (!createBy.props.isRoot && createBy.data.isVerified) {
|
||||
throw new BadDataException(
|
||||
"Domain cannot be verified during creation. Please verify the domain after creation. Please set isVerified to false.",
|
||||
);
|
||||
}
|
||||
|
||||
createBy.data.domainVerificationText =
|
||||
"oneuptime-verification-" + Text.generateRandomText(20);
|
||||
return Promise.resolve({ createBy, carryForward: null });
|
||||
@@ -41,67 +50,66 @@ export class Service extends DatabaseService<Model> {
|
||||
protected override async onBeforeUpdate(
|
||||
updateBy: UpdateBy<Model>,
|
||||
): Promise<OnUpdate<Model>> {
|
||||
if (
|
||||
updateBy.data.isVerified &&
|
||||
updateBy.query._id &&
|
||||
!updateBy.props.isRoot
|
||||
) {
|
||||
if (updateBy.data.isVerified && !updateBy.props.isRoot) {
|
||||
const projectId: FindWhere<ObjectID> | undefined =
|
||||
updateBy.query.projectId || updateBy.props.tenantId;
|
||||
|
||||
if (!projectId) {
|
||||
throw new BadDataException(
|
||||
"Project ID is required to verify the domain.",
|
||||
);
|
||||
}
|
||||
|
||||
// check the verification of the domain.
|
||||
|
||||
const items: Array<Model> = await this.findBy({
|
||||
query: {
|
||||
_id: updateBy.query._id as string,
|
||||
projectId: updateBy.props.tenantId!,
|
||||
projectId,
|
||||
...updateBy.query,
|
||||
},
|
||||
select: {
|
||||
domain: true,
|
||||
domainVerificationText: true,
|
||||
},
|
||||
|
||||
limit: 1,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (items.length === 0) {
|
||||
throw new BadDataException(
|
||||
"Domain with id " + updateBy.query._id + " not found.",
|
||||
for (const item of items) {
|
||||
const domain: string | undefined = item?.domain?.toString();
|
||||
const verificationText: string | undefined =
|
||||
item?.domainVerificationText?.toString();
|
||||
|
||||
if (!domain) {
|
||||
throw new BadDataException("Domain not found.");
|
||||
}
|
||||
|
||||
if (!verificationText) {
|
||||
throw new BadDataException(
|
||||
"Domain verification text with id " +
|
||||
updateBy.query._id +
|
||||
" not found.",
|
||||
);
|
||||
}
|
||||
|
||||
const isVerified: boolean = await Domain.verifyTxtRecord(
|
||||
domain,
|
||||
verificationText,
|
||||
);
|
||||
}
|
||||
|
||||
const domain: string | undefined = items[0]?.domain?.toString();
|
||||
const verificationText: string | undefined =
|
||||
items[0]?.domainVerificationText?.toString();
|
||||
|
||||
if (!domain) {
|
||||
throw new BadDataException(
|
||||
"Domain with id " + updateBy.query._id + " not found.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!verificationText) {
|
||||
throw new BadDataException(
|
||||
"Domain verification text with id " +
|
||||
updateBy.query._id +
|
||||
" not found.",
|
||||
);
|
||||
}
|
||||
|
||||
const isVerified: boolean = await Domain.verifyTxtRecord(
|
||||
domain,
|
||||
verificationText,
|
||||
);
|
||||
|
||||
if (!isVerified) {
|
||||
throw new BadDataException(
|
||||
"Verification TXT record " +
|
||||
verificationText +
|
||||
" not found in domain " +
|
||||
domain +
|
||||
". Please add a TXT record to verify the domain. If you have already added the TXT record, please wait for few hours to let DNS to propagate.",
|
||||
);
|
||||
if (!isVerified) {
|
||||
throw new BadDataException(
|
||||
"Verification TXT record " +
|
||||
verificationText +
|
||||
" not found in domain " +
|
||||
domain +
|
||||
". Please add a TXT record to verify the domain. If you have already added the TXT record, please wait for few hours to let DNS to propagate.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
Common/Server/Services/EnterpriseLicenseService.ts
Normal file
10
Common/Server/Services/EnterpriseLicenseService.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import DatabaseService from "./DatabaseService";
|
||||
import EnterpriseLicense from "../../Models/DatabaseModels/EnterpriseLicense";
|
||||
|
||||
export class Service extends DatabaseService<EnterpriseLicense> {
|
||||
public constructor() {
|
||||
super(EnterpriseLicense);
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
||||
@@ -19,7 +19,7 @@ export class Service extends DatabaseService<IncidentFeed> {
|
||||
super(IncidentFeed);
|
||||
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
Common/Server/Services/IncidentPostmortemTemplateService.ts
Normal file
10
Common/Server/Services/IncidentPostmortemTemplateService.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import DatabaseService from "./DatabaseService";
|
||||
import Model from "../../Models/DatabaseModels/IncidentPostmortemTemplate";
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor() {
|
||||
super(Model);
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
||||
@@ -43,14 +43,13 @@ import Metric, {
|
||||
} from "../../Models/AnalyticsModels/Metric";
|
||||
import OneUptimeDate from "../../Types/Date";
|
||||
import TelemetryUtil from "../Utils/Telemetry/Telemetry";
|
||||
import TelemetryType from "../../Types/Telemetry/TelemetryType";
|
||||
import logger from "../Utils/Logger";
|
||||
import Semaphore, {
|
||||
SemaphoreMutex,
|
||||
} from "../../Server/Infrastructure/Semaphore";
|
||||
import IncidentFeedService from "./IncidentFeedService";
|
||||
import { IncidentFeedEventType } from "../../Models/DatabaseModels/IncidentFeed";
|
||||
import { Gray500, Red500 } from "../../Types/BrandColors";
|
||||
import { Blue500, Gray500, Red500 } from "../../Types/BrandColors";
|
||||
import Label from "../../Models/DatabaseModels/Label";
|
||||
import LabelService from "./LabelService";
|
||||
import IncidentSeverity from "../../Models/DatabaseModels/IncidentSeverity";
|
||||
@@ -75,11 +74,22 @@ type UpdateCarryForward = Dictionary<{
|
||||
newMonitorChangeStatusIdTo: ObjectID | undefined;
|
||||
}>;
|
||||
|
||||
type IncidentUpdatePayload = {
|
||||
postmortemNote?: string | null;
|
||||
title?: string | null;
|
||||
rootCause?: string | null;
|
||||
description?: string | null;
|
||||
remediationNotes?: string | null;
|
||||
labels?: unknown;
|
||||
incidentSeverity?: unknown;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor() {
|
||||
super(Model);
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1298,59 +1308,103 @@ ${incident.remediationNotes || "No remediation notes provided."}
|
||||
|
||||
const projectId: ObjectID = incident!.projectId!;
|
||||
const incidentNumber: number = incident!.incidentNumber!;
|
||||
const incidentLabel: string = `Incident ${incidentNumber}`;
|
||||
const incidentLink: URL = await this.getIncidentLinkInDashboard(
|
||||
projectId,
|
||||
incidentId,
|
||||
);
|
||||
|
||||
let shouldAddIncidentFeed: boolean = false;
|
||||
let feedInfoInMarkdown: string = `**[Incident ${incidentNumber}](${(await this.getIncidentLinkInDashboard(projectId!, incidentId!)).toString()}) was updated.**`;
|
||||
const updatedIncidentData: IncidentUpdatePayload = (onUpdate.updateBy
|
||||
.data ?? {}) as IncidentUpdatePayload;
|
||||
|
||||
const createdByUserId: ObjectID | undefined | null =
|
||||
onUpdate.updateBy.props.userId;
|
||||
|
||||
if (onUpdate.updateBy.data.title) {
|
||||
// add incident feed.
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
updatedIncidentData,
|
||||
"postmortemNote",
|
||||
)
|
||||
) {
|
||||
const noteValue: string =
|
||||
(updatedIncidentData.postmortemNote as string) || "";
|
||||
const hasNoteContent: boolean = noteValue.trim().length > 0;
|
||||
|
||||
feedInfoInMarkdown += `\n\n**Title**:
|
||||
${onUpdate.updateBy.data.title || "No title provided."}
|
||||
`;
|
||||
shouldAddIncidentFeed = true;
|
||||
const postmortemFeedMarkdown: string = hasNoteContent
|
||||
? `**📘 Postmortem Note updated for [${incidentLabel}](${incidentLink.toString()})**\n\n${noteValue}`
|
||||
: `**📘 Postmortem Note cleared for [${incidentLabel}](${incidentLink.toString()})**\n\n_No postmortem note provided._`;
|
||||
|
||||
await IncidentFeedService.createIncidentFeedItem({
|
||||
incidentId,
|
||||
projectId,
|
||||
incidentFeedEventType: IncidentFeedEventType.PostmortemNote,
|
||||
displayColor: Blue500,
|
||||
feedInfoInMarkdown: postmortemFeedMarkdown,
|
||||
userId: createdByUserId || undefined,
|
||||
workspaceNotification: {
|
||||
sendWorkspaceNotification: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (onUpdate.updateBy.data.rootCause) {
|
||||
if (onUpdate.updateBy.data.title) {
|
||||
// add incident feed.
|
||||
let shouldAddIncidentFeed: boolean = false;
|
||||
let feedInfoInMarkdown: string = `**[${incidentLabel}](${incidentLink.toString()}) was updated.**`;
|
||||
|
||||
feedInfoInMarkdown += `\n\n**📄 Root Cause**:
|
||||
${onUpdate.updateBy.data.rootCause || "No root cause provided."}
|
||||
`;
|
||||
shouldAddIncidentFeed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (onUpdate.updateBy.data.description) {
|
||||
// add incident feed.
|
||||
|
||||
feedInfoInMarkdown += `\n\n**Incident Description**:
|
||||
${onUpdate.updateBy.data.description || "No description provided."}
|
||||
`;
|
||||
shouldAddIncidentFeed = true;
|
||||
}
|
||||
|
||||
if (onUpdate.updateBy.data.remediationNotes) {
|
||||
// add incident feed.
|
||||
|
||||
feedInfoInMarkdown += `\n\n**🎯 Remediation Notes**:
|
||||
${onUpdate.updateBy.data.remediationNotes || "No remediation notes provided."}
|
||||
`;
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(updatedIncidentData, "title")
|
||||
) {
|
||||
const title: string =
|
||||
(updatedIncidentData.title as string) || "No title provided.";
|
||||
feedInfoInMarkdown += `\n\n**Title**: \n${title}\n`;
|
||||
shouldAddIncidentFeed = true;
|
||||
}
|
||||
|
||||
if (
|
||||
onUpdate.updateBy.data.labels &&
|
||||
onUpdate.updateBy.data.labels.length > 0 &&
|
||||
Array.isArray(onUpdate.updateBy.data.labels)
|
||||
Object.prototype.hasOwnProperty.call(updatedIncidentData, "rootCause")
|
||||
) {
|
||||
const labelIds: Array<ObjectID> = (
|
||||
onUpdate.updateBy.data.labels as any
|
||||
const rootCause: string =
|
||||
(updatedIncidentData.rootCause as string) || "";
|
||||
const rootCauseText: string = rootCause.trim().length
|
||||
? rootCause
|
||||
: "Root cause removed.";
|
||||
feedInfoInMarkdown += `\n\n**📄 Root Cause**: \n${rootCauseText}\n`;
|
||||
shouldAddIncidentFeed = true;
|
||||
}
|
||||
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
updatedIncidentData,
|
||||
"description",
|
||||
)
|
||||
) {
|
||||
const description: string =
|
||||
(updatedIncidentData.description as string) ||
|
||||
"No description provided.";
|
||||
feedInfoInMarkdown += `\n\n**Incident Description**: \n${description}\n`;
|
||||
shouldAddIncidentFeed = true;
|
||||
}
|
||||
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
updatedIncidentData,
|
||||
"remediationNotes",
|
||||
)
|
||||
) {
|
||||
const remediationNotes: string =
|
||||
(updatedIncidentData.remediationNotes as string) || "";
|
||||
const remediationText: string = remediationNotes.trim().length
|
||||
? remediationNotes
|
||||
: "Remediation notes removed.";
|
||||
feedInfoInMarkdown += `\n\n**🎯 Remediation Notes**: \n${remediationText}\n`;
|
||||
shouldAddIncidentFeed = true;
|
||||
}
|
||||
|
||||
if (
|
||||
updatedIncidentData.labels &&
|
||||
(updatedIncidentData.labels as Array<Label>).length > 0 &&
|
||||
Array.isArray(updatedIncidentData.labels)
|
||||
) {
|
||||
const labelIds: Array<ObjectID> = (updatedIncidentData.labels as any)
|
||||
.map((label: Label) => {
|
||||
if (label._id) {
|
||||
return new ObjectID(label._id?.toString());
|
||||
@@ -1391,16 +1445,14 @@ ${labels
|
||||
}
|
||||
|
||||
if (
|
||||
onUpdate.updateBy.data.incidentSeverity &&
|
||||
(onUpdate.updateBy.data.incidentSeverity as any)._id
|
||||
updatedIncidentData.incidentSeverity &&
|
||||
(updatedIncidentData.incidentSeverity as any)._id
|
||||
) {
|
||||
const incidentSeverity: IncidentSeverity | null =
|
||||
await IncidentSeverityService.findOneBy({
|
||||
query: {
|
||||
_id: new ObjectID(
|
||||
(
|
||||
onUpdate.updateBy.data.incidentSeverity as any
|
||||
)?._id.toString(),
|
||||
(updatedIncidentData.incidentSeverity as any)?._id.toString(),
|
||||
),
|
||||
},
|
||||
select: {
|
||||
@@ -1957,6 +2009,9 @@ ${incidentSeverity.name}
|
||||
incidentSeverityId: incident.incidentSeverity?._id?.toString(),
|
||||
incidentSeverityName: incident.incidentSeverity?.name?.toString(),
|
||||
};
|
||||
incidentCountMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
||||
incidentCountMetric.attributes,
|
||||
);
|
||||
|
||||
incidentCountMetric.time = incidentStartsAt;
|
||||
incidentCountMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
||||
@@ -2014,6 +2069,9 @@ ${incidentSeverity.name}
|
||||
incidentSeverityId: incident.incidentSeverity?._id?.toString(),
|
||||
incidentSeverityName: incident.incidentSeverity?.name?.toString(),
|
||||
};
|
||||
timeToAcknowledgeMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
||||
timeToAcknowledgeMetric.attributes,
|
||||
);
|
||||
|
||||
timeToAcknowledgeMetric.time =
|
||||
ackIncidentStateTimeline?.startsAt ||
|
||||
@@ -2076,6 +2134,9 @@ ${incidentSeverity.name}
|
||||
incidentSeverityId: incident.incidentSeverity?._id?.toString(),
|
||||
incidentSeverityName: incident.incidentSeverity?.name?.toString(),
|
||||
};
|
||||
timeToResolveMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
||||
timeToResolveMetric.attributes,
|
||||
);
|
||||
|
||||
timeToResolveMetric.time =
|
||||
resolvedIncidentStateTimeline?.startsAt ||
|
||||
@@ -2133,6 +2194,9 @@ ${incidentSeverity.name}
|
||||
incidentSeverityId: incident.incidentSeverity?._id?.toString(),
|
||||
incidentSeverityName: incident.incidentSeverity?.name?.toString(),
|
||||
};
|
||||
incidentDurationMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
||||
incidentDurationMetric.attributes,
|
||||
);
|
||||
|
||||
incidentDurationMetric.time =
|
||||
lastIncidentStateTimeline?.startsAt ||
|
||||
@@ -2162,15 +2226,6 @@ ${incidentSeverity.name}
|
||||
},
|
||||
});
|
||||
|
||||
// index attributes.
|
||||
TelemetryUtil.indexAttributes({
|
||||
attributes: ["monitorIds", "projectId", "incidentId", "monitorNames"],
|
||||
projectId: incident.projectId,
|
||||
telemetryType: TelemetryType.Metric,
|
||||
}).catch((err: Error) => {
|
||||
logger.error(err);
|
||||
});
|
||||
|
||||
TelemetryUtil.indexMetricNameServiceNameMap({
|
||||
metricNameServiceNameMap: metricTypesMap,
|
||||
projectId: incident.projectId,
|
||||
|
||||
@@ -31,7 +31,7 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
|
||||
public constructor() {
|
||||
super(IncidentStateTimeline);
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("startsAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("startsAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ import ProjectService from "./ProjectService";
|
||||
import ProjectSmtpConfigService from "./ProjectSmtpConfigService";
|
||||
import ProjectSsoService from "./ProjectSsoService";
|
||||
import PromoCodeService from "./PromoCodeService";
|
||||
import EnterpriseLicenseService from "./EnterpriseLicenseService";
|
||||
import ResellerPlanService from "./ResellerPlanService";
|
||||
import ResellerService from "./ResellerService";
|
||||
import ScheduledMaintenanceCustomFieldService from "./ScheduledMaintenanceCustomFieldService";
|
||||
@@ -135,7 +136,6 @@ import WorkflowVariablesService from "./WorkflowVariableService";
|
||||
import AnalyticsBaseModel from "../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
|
||||
import CopilotPullRequestService from "./CopilotPullRequestService";
|
||||
import ServiceCatalogDependencyService from "./ServiceCatalogDependencyService";
|
||||
import TelemetryAttributeService from "./TelemetryAttributeService";
|
||||
import TelemetryExceptionService from "./TelemetryExceptionService";
|
||||
import ExceptionInstanceService from "./ExceptionInstanceService";
|
||||
import CopilotActionTypePriorityService from "./CopilotActionTypePriorityService";
|
||||
@@ -174,6 +174,7 @@ const services: Array<BaseService> = [
|
||||
OnCallDutyPolicyTimeLogService,
|
||||
AcmeCertificateService,
|
||||
PromoCodeService,
|
||||
EnterpriseLicenseService,
|
||||
|
||||
ResellerService,
|
||||
ResellerPlanService,
|
||||
@@ -356,7 +357,6 @@ export const AnalyticsServices: Array<
|
||||
LogService,
|
||||
SpanService,
|
||||
MetricService,
|
||||
TelemetryAttributeService,
|
||||
ExceptionInstanceService,
|
||||
MonitorLogService,
|
||||
];
|
||||
|
||||
@@ -19,7 +19,7 @@ export class Service extends DatabaseService<MonitorFeed> {
|
||||
super(MonitorFeed);
|
||||
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,6 @@ import ObjectID from "../../Types/ObjectID";
|
||||
import Metric, {
|
||||
AggregationTemporality,
|
||||
} from "../../Models/AnalyticsModels/Metric";
|
||||
import Dictionary from "../../Types/Dictionary";
|
||||
import ProductType from "../../Types/MeteredPlan/ProductType";
|
||||
import { IsBillingEnabled } from "../../Server/EnvironmentConfig";
|
||||
import TelemetryUsageBillingService from "../../Server/Services/TelemetryUsageBillingService";
|
||||
import logger from "../../Server/Utils/Logger";
|
||||
import TelemetryService from "../../Models/DatabaseModels/TelemetryService";
|
||||
import TelemetryServiceService from "../../Server/Services/TelemetryServiceService";
|
||||
import { DEFAULT_RETENTION_IN_DAYS } from "../../Models/DatabaseModels/TelemetryUsageBilling";
|
||||
@@ -20,10 +15,9 @@ export enum OtelAggregationTemporality {
|
||||
Delta = "AGGREGATION_TEMPORALITY_DELTA",
|
||||
}
|
||||
|
||||
export interface TelemetryServiceDataIngested {
|
||||
export interface TelemetryServiceMetadata {
|
||||
serviceName: string;
|
||||
serviceId: ObjectID;
|
||||
dataIngestedInGB: number;
|
||||
dataRententionInDays: number;
|
||||
}
|
||||
|
||||
@@ -80,38 +74,6 @@ export default class OTelIngestService {
|
||||
service.retainTelemetryDataForDays || DEFAULT_RETENTION_IN_DAYS,
|
||||
};
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public static async recordDataIngestedUsgaeBilling(data: {
|
||||
services: Dictionary<TelemetryServiceDataIngested>;
|
||||
projectId: ObjectID;
|
||||
productType: ProductType;
|
||||
}): Promise<void> {
|
||||
if (!IsBillingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const serviceName in data.services) {
|
||||
const serviceData: TelemetryServiceDataIngested | undefined =
|
||||
data.services[serviceName];
|
||||
|
||||
if (!serviceData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TelemetryUsageBillingService.updateUsageBilling({
|
||||
projectId: data.projectId,
|
||||
productType: data.productType,
|
||||
dataIngestedInGB: serviceData.dataIngestedInGB || 0,
|
||||
telemetryServiceId: serviceData.serviceId,
|
||||
retentionInDays: serviceData.dataRententionInDays,
|
||||
}).catch((err: Error) => {
|
||||
logger.error("Failed to update usage billing for OTel");
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public static getMetricFromDatapoint(data: {
|
||||
dbMetric: Metric;
|
||||
@@ -212,6 +174,10 @@ export default class OTelIngestService {
|
||||
};
|
||||
}
|
||||
|
||||
newDbMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
||||
newDbMetric.attributes,
|
||||
);
|
||||
|
||||
// aggregationTemporality
|
||||
|
||||
if (aggregationTemporality) {
|
||||
|
||||
@@ -71,6 +71,8 @@ import URL from "../../Types/API/URL";
|
||||
import Exception from "../../Types/Exception/Exception";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
import DatabaseConfig from "../DatabaseConfig";
|
||||
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
||||
import PositiveNumber from "../../Types/PositiveNumber";
|
||||
|
||||
export interface CurrentPlan {
|
||||
plan: PlanType | null;
|
||||
@@ -321,145 +323,134 @@ export class ProjectService extends DatabaseService<Model> {
|
||||
);
|
||||
}
|
||||
|
||||
if (updateBy.data.paymentProviderPlanId) {
|
||||
// payment provider id changed.
|
||||
const project: Model | null = await this.findOneById({
|
||||
id: new ObjectID(updateBy.query._id! as string),
|
||||
select: {
|
||||
paymentProviderSubscriptionId: true,
|
||||
paymentProviderMeteredSubscriptionId: true,
|
||||
paymentProviderSubscriptionSeats: true,
|
||||
paymentProviderPlanId: true,
|
||||
trialEndsAt: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new BadDataException("Project not found");
|
||||
}
|
||||
|
||||
if (
|
||||
project.paymentProviderPlanId !== updateBy.data.paymentProviderPlanId
|
||||
) {
|
||||
logger.debug("Changing plan for project " + project.id);
|
||||
|
||||
const plan: SubscriptionPlan | undefined =
|
||||
SubscriptionPlan.getSubscriptionPlanById(
|
||||
updateBy.data.paymentProviderPlanId! as string,
|
||||
getAllEnvVars(),
|
||||
);
|
||||
|
||||
if (!plan) {
|
||||
throw new BadDataException("Invalid plan");
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
"Changing plan for project " +
|
||||
project.id?.toString() +
|
||||
" to " +
|
||||
plan.getName(),
|
||||
);
|
||||
|
||||
if (!project.paymentProviderSubscriptionSeats) {
|
||||
project.paymentProviderSubscriptionSeats =
|
||||
await TeamMemberService.getUniqueTeamMemberCountInProject(
|
||||
project.id!,
|
||||
);
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
"Changing plan for project " +
|
||||
project.id?.toString() +
|
||||
" to " +
|
||||
plan.getName() +
|
||||
" with seats " +
|
||||
project.paymentProviderSubscriptionSeats,
|
||||
);
|
||||
|
||||
const subscription: {
|
||||
subscriptionId: string;
|
||||
meteredSubscriptionId: string;
|
||||
trialEndsAt?: Date | undefined;
|
||||
} = await BillingService.changePlan({
|
||||
projectId: project.id!,
|
||||
subscriptionId: project.paymentProviderSubscriptionId as string,
|
||||
meteredSubscriptionId:
|
||||
project.paymentProviderMeteredSubscriptionId as string,
|
||||
serverMeteredPlans: AllMeteredPlans,
|
||||
newPlan: plan,
|
||||
quantity: project.paymentProviderSubscriptionSeats as number,
|
||||
isYearly:
|
||||
plan.getYearlyPlanId() === updateBy.data.paymentProviderPlanId,
|
||||
endTrialAt: project.trialEndsAt,
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
"Changing plan for project " +
|
||||
project.id?.toString() +
|
||||
" to " +
|
||||
plan.getName() +
|
||||
" with seats " +
|
||||
project.paymentProviderSubscriptionSeats +
|
||||
" completed.",
|
||||
);
|
||||
|
||||
// refresh subscription status.
|
||||
const subscriptionState: SubscriptionStatus =
|
||||
await BillingService.getSubscriptionStatus(
|
||||
subscription.subscriptionId as string,
|
||||
);
|
||||
|
||||
const meteredSubscriptionState: SubscriptionStatus =
|
||||
await BillingService.getSubscriptionStatus(
|
||||
subscription.meteredSubscriptionId as string,
|
||||
);
|
||||
|
||||
await this.updateOneById({
|
||||
id: new ObjectID(updateBy.query._id! as string),
|
||||
data: {
|
||||
paymentProviderSubscriptionId: subscription.subscriptionId,
|
||||
paymentProviderMeteredSubscriptionId:
|
||||
subscription.meteredSubscriptionId,
|
||||
trialEndsAt: subscription.trialEndsAt || new Date(),
|
||||
planName: SubscriptionPlan.getPlanType(
|
||||
updateBy.data.paymentProviderPlanId! as string,
|
||||
),
|
||||
paymentProviderMeteredSubscriptionStatus:
|
||||
meteredSubscriptionState,
|
||||
paymentProviderSubscriptionStatus: subscriptionState,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
ignoreHooks: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
"Changing plan for project " +
|
||||
project.id?.toString() +
|
||||
" to " +
|
||||
plan.getName() +
|
||||
" with seats " +
|
||||
project.paymentProviderSubscriptionSeats +
|
||||
" completed and project updated.",
|
||||
);
|
||||
|
||||
if (project.id) {
|
||||
// send slack message on plan change.
|
||||
await this.sendSubscriptionChangeWebhookSlackNotification(
|
||||
project.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (
|
||||
updateBy.data.paymentProviderPlanId &&
|
||||
!updateBy.props.ignoreHooks &&
|
||||
!updateBy.props.isRoot
|
||||
) {
|
||||
throw new BadDataException(
|
||||
"Project plan cannot be updated directly. Please use the change plan API.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return { updateBy, carryForward: [] };
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async changePlan(params: {
|
||||
projectId: ObjectID;
|
||||
paymentProviderPlanId: string;
|
||||
endTrialAt?: Date | null;
|
||||
}): Promise<void> {
|
||||
if (!IsBillingEnabled) {
|
||||
throw new BadDataException("Billing is not enabled for this server");
|
||||
}
|
||||
|
||||
const project: Model | null = await this.findOneById({
|
||||
id: params.projectId,
|
||||
select: {
|
||||
_id: true,
|
||||
paymentProviderSubscriptionId: true,
|
||||
paymentProviderMeteredSubscriptionId: true,
|
||||
paymentProviderSubscriptionSeats: true,
|
||||
paymentProviderPlanId: true,
|
||||
trialEndsAt: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new BadDataException("Project not found");
|
||||
}
|
||||
|
||||
if (!project.paymentProviderSubscriptionId) {
|
||||
throw new BadDataException("Payment Provider subscription not found");
|
||||
}
|
||||
|
||||
if (!project.paymentProviderMeteredSubscriptionId) {
|
||||
throw new BadDataException(
|
||||
"Payment Provider metered subscription not found",
|
||||
);
|
||||
}
|
||||
|
||||
const plan: SubscriptionPlan | undefined =
|
||||
SubscriptionPlan.getSubscriptionPlanById(
|
||||
params.paymentProviderPlanId,
|
||||
getAllEnvVars(),
|
||||
);
|
||||
|
||||
if (!plan) {
|
||||
throw new BadDataException("Invalid plan");
|
||||
}
|
||||
|
||||
let seats: number | undefined = project.paymentProviderSubscriptionSeats;
|
||||
|
||||
if (!seats || seats <= 0) {
|
||||
seats = await TeamMemberService.getUniqueTeamMemberCountInProject(
|
||||
project.id!,
|
||||
);
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`Changing plan for project ${project.id?.toString()} to ${plan.getName()} with seats ${seats}`,
|
||||
);
|
||||
|
||||
const endTrialAt: Date | undefined =
|
||||
params.endTrialAt !== undefined
|
||||
? params.endTrialAt || undefined
|
||||
: project.trialEndsAt || undefined;
|
||||
|
||||
const subscription: {
|
||||
subscriptionId: string;
|
||||
meteredSubscriptionId: string;
|
||||
trialEndsAt?: Date | undefined;
|
||||
} = await BillingService.changePlan({
|
||||
projectId: project.id!,
|
||||
subscriptionId: project.paymentProviderSubscriptionId,
|
||||
meteredSubscriptionId: project.paymentProviderMeteredSubscriptionId,
|
||||
serverMeteredPlans: AllMeteredPlans,
|
||||
newPlan: plan,
|
||||
quantity: seats,
|
||||
isYearly: plan.getYearlyPlanId() === params.paymentProviderPlanId,
|
||||
endTrialAt: endTrialAt,
|
||||
});
|
||||
|
||||
const subscriptionState: SubscriptionStatus =
|
||||
await BillingService.getSubscriptionStatus(subscription.subscriptionId);
|
||||
|
||||
const meteredSubscriptionState: SubscriptionStatus =
|
||||
await BillingService.getSubscriptionStatus(
|
||||
subscription.meteredSubscriptionId,
|
||||
);
|
||||
|
||||
await this.updateOneById({
|
||||
id: project.id!,
|
||||
data: {
|
||||
paymentProviderPlanId: params.paymentProviderPlanId,
|
||||
paymentProviderSubscriptionId: subscription.subscriptionId,
|
||||
paymentProviderMeteredSubscriptionId:
|
||||
subscription.meteredSubscriptionId,
|
||||
paymentProviderSubscriptionSeats: seats,
|
||||
trialEndsAt: subscription.trialEndsAt || endTrialAt || new Date(),
|
||||
planName: SubscriptionPlan.getPlanType(
|
||||
params.paymentProviderPlanId,
|
||||
getAllEnvVars(),
|
||||
),
|
||||
paymentProviderMeteredSubscriptionStatus: meteredSubscriptionState,
|
||||
paymentProviderSubscriptionStatus: subscriptionState,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
ignoreHooks: true,
|
||||
},
|
||||
});
|
||||
|
||||
await this.sendSubscriptionChangeWebhookSlackNotification(project.id!);
|
||||
}
|
||||
|
||||
private async sendSubscriptionChangeWebhookSlackNotification(
|
||||
projectId: ObjectID,
|
||||
): Promise<void> {
|
||||
@@ -1446,6 +1437,28 @@ export class ProjectService extends DatabaseService<Model> {
|
||||
};
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async getAllActiveProjects(params?: {
|
||||
select?: Select<Model>;
|
||||
props?: DatabaseCommonInteractionProps;
|
||||
skip?: PositiveNumber | number;
|
||||
limit?: PositiveNumber | number;
|
||||
}): Promise<Array<Model>> {
|
||||
const select: Select<Model> | undefined =
|
||||
params?.select || ({ _id: true } as Select<Model>);
|
||||
const props: DatabaseCommonInteractionProps = params?.props || {
|
||||
isRoot: true,
|
||||
};
|
||||
|
||||
return await this.findAllBy({
|
||||
query: this.getActiveProjectStatusQuery(),
|
||||
select,
|
||||
props,
|
||||
skip: params?.skip,
|
||||
limit: params?.limit,
|
||||
});
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async getProjectLinkInDashboard(projectId: ObjectID): Promise<URL> {
|
||||
const dashboardUrl: URL = await DatabaseConfig.getDashboardUrl();
|
||||
|
||||
@@ -19,7 +19,7 @@ export class Service extends DatabaseService<Model> {
|
||||
super(Model);
|
||||
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export class Service extends DatabaseService<Model> {
|
||||
public constructor() {
|
||||
super(Model);
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline>
|
||||
public constructor() {
|
||||
super(ScheduledMaintenanceStateTimeline);
|
||||
if (IsBillingEnabled) {
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 120);
|
||||
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ import CreateBy from "../Types/Database/CreateBy";
|
||||
import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
|
||||
import UpdateBy from "../Types/Database/UpdateBy";
|
||||
import CookieUtil from "../Utils/Cookie";
|
||||
import { ExpressRequest } from "../Utils/Express";
|
||||
import JSONWebToken from "../Utils/JsonWebToken";
|
||||
import { ExpressRequest, ExpressResponse } from "../Utils/Express";
|
||||
import JSONWebToken, {
|
||||
RefreshTokenData,
|
||||
} from "../Utils/JsonWebToken";
|
||||
import logger from "../Utils/Logger";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
import DatabaseService from "./DatabaseService";
|
||||
@@ -24,6 +26,7 @@ import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import JSONWebTokenData from "../../Types/JsonWebTokenData";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import PositiveNumber from "../../Types/PositiveNumber";
|
||||
import HashedString from "../../Types/HashedString";
|
||||
import Typeof from "../../Types/Typeof";
|
||||
import MonitorStatus from "../../Models/DatabaseModels/MonitorStatus";
|
||||
import StatusPage from "../../Models/DatabaseModels/StatusPage";
|
||||
@@ -61,6 +64,9 @@ import IP from "../../Types/IP/IP";
|
||||
import NotAuthenticatedException from "../../Types/Exception/NotAuthenticatedException";
|
||||
import ForbiddenException from "../../Types/Exception/ForbiddenException";
|
||||
import CommonAPI from "../API/CommonAPI";
|
||||
import StatusPagePrivateUserService from "./StatusPagePrivateUserService";
|
||||
import StatusPagePrivateUser from "../../Models/DatabaseModels/StatusPagePrivateUser";
|
||||
import { EncryptionSecret } from "../EnvironmentConfig";
|
||||
|
||||
export interface StatusPageReportItem {
|
||||
resourceName: string;
|
||||
@@ -369,12 +375,14 @@ export class Service extends DatabaseService<StatusPage> {
|
||||
public async hasReadAccess(data: {
|
||||
statusPageId: ObjectID;
|
||||
req: ExpressRequest;
|
||||
res: ExpressResponse;
|
||||
}): Promise<{
|
||||
hasReadAccess: boolean;
|
||||
error?: NotAuthenticatedException | ForbiddenException;
|
||||
}> {
|
||||
const statusPageId: ObjectID = data.statusPageId;
|
||||
const req: ExpressRequest = data.req;
|
||||
const res: ExpressResponse = data.res;
|
||||
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
||||
@@ -446,20 +454,37 @@ export class Service extends DatabaseService<StatusPage> {
|
||||
CookieUtil.getUserTokenKey(statusPageId),
|
||||
);
|
||||
|
||||
let decoded: JSONWebTokenData | null = null;
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const decoded: JSONWebTokenData = JSONWebToken.decode(
|
||||
token as string,
|
||||
);
|
||||
|
||||
if (decoded.statusPageId?.toString() === statusPageId.toString()) {
|
||||
return {
|
||||
hasReadAccess: true,
|
||||
};
|
||||
}
|
||||
decoded = JSONWebToken.decode(token as string);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Invalid status page access token, attempting refresh: ${
|
||||
error.message || "unknown error"
|
||||
}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
decoded = await this.tryRefreshStatusPageSession({
|
||||
statusPageId,
|
||||
req,
|
||||
res,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
decoded = await this.tryRefreshStatusPageSession({
|
||||
statusPageId,
|
||||
req,
|
||||
res,
|
||||
});
|
||||
}
|
||||
|
||||
if (decoded && decoded.statusPageId?.toString() === statusPageId.toString()) {
|
||||
return {
|
||||
hasReadAccess: true,
|
||||
};
|
||||
}
|
||||
|
||||
// if it does not have public access, check if this user has access.
|
||||
@@ -493,6 +518,121 @@ export class Service extends DatabaseService<StatusPage> {
|
||||
};
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
private async tryRefreshStatusPageSession(data: {
|
||||
statusPageId: ObjectID;
|
||||
req: ExpressRequest;
|
||||
res: ExpressResponse;
|
||||
}): Promise<JSONWebTokenData | null> {
|
||||
const { statusPageId, req, res } = data;
|
||||
|
||||
const refreshTokenKey: string = CookieUtil.getRefreshTokenKey(statusPageId);
|
||||
const accessTokenKey: string = CookieUtil.getUserTokenKey(statusPageId);
|
||||
|
||||
const refreshToken: string | undefined = CookieUtil.getCookieFromExpressRequest(
|
||||
req,
|
||||
refreshTokenKey,
|
||||
);
|
||||
|
||||
if (!refreshToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let refreshTokenData: RefreshTokenData;
|
||||
|
||||
try {
|
||||
refreshTokenData = JSONWebToken.decodeRefreshToken(refreshToken);
|
||||
} catch (err) {
|
||||
const error: Error = err as Error;
|
||||
logger.warn(
|
||||
`Failed to decode status page refresh token during middleware refresh: ${
|
||||
error.message || "unknown error"
|
||||
}`,
|
||||
);
|
||||
logger.debug(error);
|
||||
CookieUtil.removeCookie(res, refreshTokenKey);
|
||||
CookieUtil.removeCookie(res, accessTokenKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
!refreshTokenData.statusPageId ||
|
||||
refreshTokenData.statusPageId.toString() !== statusPageId.toString()
|
||||
) {
|
||||
CookieUtil.removeCookie(res, refreshTokenKey);
|
||||
CookieUtil.removeCookie(res, accessTokenKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
const hashedSessionId: string = await HashedString.hashValue(
|
||||
refreshTokenData.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
const user: StatusPagePrivateUser | null =
|
||||
await StatusPagePrivateUserService.findOneBy({
|
||||
query: {
|
||||
_id: refreshTokenData.userId,
|
||||
statusPageId: statusPageId,
|
||||
jwtRefreshToken: hashedSessionId,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
email: true,
|
||||
statusPageId: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
CookieUtil.removeCookie(res, refreshTokenKey);
|
||||
CookieUtil.removeCookie(res, accessTokenKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
const session = CookieUtil.setStatusPageUserCookie({
|
||||
expressResponse: res,
|
||||
user: user,
|
||||
statusPageId: statusPageId,
|
||||
});
|
||||
|
||||
if (!req.cookies) {
|
||||
req.cookies = {} as Dictionary<string>;
|
||||
}
|
||||
|
||||
req.cookies[accessTokenKey] = session.accessToken;
|
||||
req.cookies[refreshTokenKey] = session.refreshToken;
|
||||
|
||||
const hashedNewSessionId: string = await HashedString.hashValue(
|
||||
session.sessionId,
|
||||
EncryptionSecret,
|
||||
);
|
||||
|
||||
await StatusPagePrivateUserService.updateOneBy({
|
||||
query: {
|
||||
_id: user.id!,
|
||||
statusPageId: statusPageId,
|
||||
},
|
||||
data: {
|
||||
jwtRefreshToken: hashedNewSessionId,
|
||||
lastActive: OneUptimeDate.getCurrentDate(),
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`Status page session refreshed automatically for ${
|
||||
user.email?.toString() || user.id?.toString() || "unknown"
|
||||
} on status page ${statusPageId.toString()}`,
|
||||
);
|
||||
|
||||
return JSONWebToken.decode(session.accessToken);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async getMonitorStatusTimelineForStatusPage(data: {
|
||||
monitorIds: Array<ObjectID>;
|
||||
@@ -1150,5 +1290,122 @@ export class Service extends DatabaseService<StatusPage> {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async getMonitorGroupCurrentStatuses(data: {
|
||||
statusPageResources: Array<StatusPageResource>;
|
||||
monitorStatuses: Array<MonitorStatus>;
|
||||
}): Promise<Dictionary<ObjectID>> {
|
||||
const monitorGroupCurrentStatuses: Dictionary<ObjectID> = {};
|
||||
|
||||
for (const resource of data.statusPageResources) {
|
||||
if (resource.monitorGroupId) {
|
||||
const monitorGroupResources: Array<MonitorGroupResource> =
|
||||
await MonitorGroupResourceService.findBy({
|
||||
query: {
|
||||
monitorGroupId: resource.monitorGroupId,
|
||||
},
|
||||
select: {
|
||||
monitorId: true,
|
||||
monitor: {
|
||||
currentMonitorStatusId: true,
|
||||
},
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const statuses: Array<ObjectID> = monitorGroupResources
|
||||
.filter((item: MonitorGroupResource) => {
|
||||
return (
|
||||
item.monitor &&
|
||||
item.monitor.currentMonitorStatusId &&
|
||||
item.monitorId
|
||||
);
|
||||
})
|
||||
.map((item: MonitorGroupResource) => {
|
||||
return item.monitor!.currentMonitorStatusId!;
|
||||
});
|
||||
|
||||
let worstStatus: MonitorStatus | null = null;
|
||||
|
||||
for (const statusId of statuses) {
|
||||
const status: MonitorStatus | undefined = data.monitorStatuses.find(
|
||||
(status: MonitorStatus) => {
|
||||
return status._id?.toString() === statusId.toString();
|
||||
},
|
||||
);
|
||||
|
||||
if (
|
||||
status &&
|
||||
(!worstStatus || status.priority! < worstStatus.priority!)
|
||||
) {
|
||||
worstStatus = status;
|
||||
}
|
||||
}
|
||||
|
||||
if (worstStatus && worstStatus._id) {
|
||||
monitorGroupCurrentStatuses[resource.monitorGroupId.toString()] =
|
||||
new ObjectID(worstStatus._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return monitorGroupCurrentStatuses;
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public getOverallMonitorStatus(data: {
|
||||
statusPageResources: Array<StatusPageResource>;
|
||||
monitorStatuses: Array<MonitorStatus>;
|
||||
monitorGroupCurrentStatuses: Dictionary<ObjectID>;
|
||||
}): MonitorStatus | null {
|
||||
let currentStatus: MonitorStatus | null =
|
||||
data.monitorStatuses.length > 0 && data.monitorStatuses[0]
|
||||
? data.monitorStatuses[0]
|
||||
: null;
|
||||
|
||||
const dict: Dictionary<number> = {};
|
||||
|
||||
for (const resource of data.statusPageResources) {
|
||||
if (resource.monitor?.currentMonitorStatusId) {
|
||||
if (
|
||||
!Object.keys(dict).includes(
|
||||
resource.monitor?.currentMonitorStatusId.toString() || "",
|
||||
)
|
||||
) {
|
||||
dict[resource.monitor?.currentMonitorStatusId?.toString()] = 1;
|
||||
} else {
|
||||
dict[resource.monitor!.currentMonitorStatusId!.toString()]!++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check status of monitor groups.
|
||||
|
||||
for (const groupId in data.monitorGroupCurrentStatuses) {
|
||||
const statusId: ObjectID | undefined =
|
||||
data.monitorGroupCurrentStatuses[groupId];
|
||||
|
||||
if (statusId) {
|
||||
if (!Object.keys(dict).includes(statusId.toString() || "")) {
|
||||
dict[statusId.toString()] = 1;
|
||||
} else {
|
||||
dict[statusId.toString()]!++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const monitorStatus of data.monitorStatuses) {
|
||||
if (monitorStatus._id && dict[monitorStatus._id]) {
|
||||
currentStatus = monitorStatus;
|
||||
}
|
||||
}
|
||||
|
||||
return currentStatus;
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
|
||||
@@ -21,6 +21,7 @@ import { AccountsRoute } from "../../ServiceRoute";
|
||||
import Hostname from "../../Types/API/Hostname";
|
||||
import Protocol from "../../Types/API/Protocol";
|
||||
import URL from "../../Types/API/URL";
|
||||
import Route from "../../Types/API/Route";
|
||||
import SubscriptionPlan, {
|
||||
PlanType,
|
||||
} from "../../Types/Billing/SubscriptionPlan";
|
||||
@@ -151,10 +152,18 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
|
||||
templateType: EmailTemplateType.InviteMember,
|
||||
vars: {
|
||||
signInLink: URL.fromString(
|
||||
new URL(httpProtocol, host, AccountsRoute).toString(),
|
||||
new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
new Route(AccountsRoute.toString()),
|
||||
).toString(),
|
||||
).toString(),
|
||||
registerLink: URL.fromString(
|
||||
new URL(httpProtocol, host, AccountsRoute).toString(),
|
||||
new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
new Route(AccountsRoute.toString()),
|
||||
).toString(),
|
||||
)
|
||||
.addRoute("/register")
|
||||
.addQueryParam("email", email.toString(), true)
|
||||
|
||||
@@ -1,13 +1,70 @@
|
||||
import { SQL, Statement } from "../Utils/AnalyticsDatabase/Statement";
|
||||
import TelemetryType from "../../Types/Telemetry/TelemetryType";
|
||||
import ClickhouseDatabase from "../Infrastructure/ClickhouseDatabase";
|
||||
import AnalyticsDatabaseService from "./AnalyticsDatabaseService";
|
||||
import TelemetryAttribute from "../../Models/AnalyticsModels/TelemetryAttribute";
|
||||
import LogDatabaseService from "./LogService";
|
||||
import MetricDatabaseService from "./MetricService";
|
||||
import SpanDatabaseService from "./SpanService";
|
||||
import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
|
||||
import { JSONObject } from "../../Types/JSON";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import OneUptimeDate from "../../Types/Date";
|
||||
import GlobalCache from "../Infrastructure/GlobalCache";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
import AnalyticsDatabaseService, {
|
||||
DbJSONResponse,
|
||||
Results,
|
||||
} from "./AnalyticsDatabaseService";
|
||||
|
||||
export class TelemetryAttributeService extends AnalyticsDatabaseService<TelemetryAttribute> {
|
||||
public constructor(clickhouseDatabase?: ClickhouseDatabase | undefined) {
|
||||
super({ modelType: TelemetryAttribute, database: clickhouseDatabase });
|
||||
type TelemetrySource = {
|
||||
service: AnalyticsDatabaseService<any>;
|
||||
tableName: string;
|
||||
attributesColumn: string;
|
||||
attributeKeysColumn: string;
|
||||
timeColumn: string;
|
||||
};
|
||||
|
||||
type TelemetryAttributesCacheEntry = {
|
||||
attributes: Array<string>;
|
||||
refreshedAt: Date;
|
||||
};
|
||||
|
||||
export class TelemetryAttributeService {
|
||||
private static readonly ATTRIBUTES_LIMIT: number = 5000;
|
||||
private static readonly ROW_SCAN_LIMIT: number = 10000;
|
||||
private static readonly CACHE_NAMESPACE: string = "telemetry-attributes";
|
||||
private static readonly CACHE_STALE_AFTER_MINUTES: number = 5;
|
||||
private static readonly LOOKBACK_WINDOW_IN_DAYS: number = 30;
|
||||
|
||||
private getTelemetrySource(
|
||||
telemetryType: TelemetryType,
|
||||
): TelemetrySource | null {
|
||||
switch (telemetryType) {
|
||||
case TelemetryType.Log:
|
||||
return {
|
||||
service: LogDatabaseService,
|
||||
tableName: LogDatabaseService.model.tableName,
|
||||
attributesColumn: "attributes",
|
||||
attributeKeysColumn: "attributeKeys",
|
||||
timeColumn: "time",
|
||||
};
|
||||
case TelemetryType.Metric:
|
||||
return {
|
||||
service: MetricDatabaseService,
|
||||
tableName: MetricDatabaseService.model.tableName,
|
||||
attributesColumn: "attributes",
|
||||
attributeKeysColumn: "attributeKeys",
|
||||
timeColumn: "time",
|
||||
};
|
||||
case TelemetryType.Trace:
|
||||
return {
|
||||
service: SpanDatabaseService,
|
||||
tableName: SpanDatabaseService.model.tableName,
|
||||
attributesColumn: "attributes",
|
||||
attributeKeysColumn: "attributeKeys",
|
||||
timeColumn: "startTime",
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -15,57 +72,230 @@ export class TelemetryAttributeService extends AnalyticsDatabaseService<Telemetr
|
||||
projectId: ObjectID;
|
||||
telemetryType: TelemetryType;
|
||||
}): Promise<string[]> {
|
||||
const telemetryAttribute: TelemetryAttribute | null = await this.findOneBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
telemetryType: data.telemetryType,
|
||||
},
|
||||
select: {
|
||||
attributes: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
const source: TelemetrySource | null = this.getTelemetrySource(
|
||||
data.telemetryType,
|
||||
);
|
||||
|
||||
return telemetryAttribute &&
|
||||
telemetryAttribute.attributes &&
|
||||
telemetryAttribute
|
||||
? telemetryAttribute.attributes
|
||||
: [];
|
||||
if (!source) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const cacheKey: string = TelemetryAttributeService.getCacheKey(
|
||||
data.projectId,
|
||||
data.telemetryType,
|
||||
);
|
||||
|
||||
const cachedEntry: TelemetryAttributesCacheEntry | null =
|
||||
await TelemetryAttributeService.getCachedAttributes(cacheKey);
|
||||
|
||||
if (cachedEntry && TelemetryAttributeService.isCacheFresh(cachedEntry)) {
|
||||
return cachedEntry.attributes;
|
||||
}
|
||||
|
||||
let attributes: Array<string> = [];
|
||||
|
||||
try {
|
||||
attributes = await TelemetryAttributeService.fetchAttributesFromDatabase({
|
||||
projectId: data.projectId,
|
||||
source,
|
||||
});
|
||||
} catch (error) {
|
||||
if (cachedEntry) {
|
||||
return cachedEntry.attributes;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
await TelemetryAttributeService.storeAttributesInCache(
|
||||
cacheKey,
|
||||
attributes,
|
||||
);
|
||||
|
||||
if (attributes.length === 0 && cachedEntry) {
|
||||
return cachedEntry.attributes;
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async refreshAttributes(data: {
|
||||
private static getCacheKey(
|
||||
projectId: ObjectID,
|
||||
telemetryType: TelemetryType,
|
||||
): string {
|
||||
return `${projectId.toString()}:${telemetryType}`;
|
||||
}
|
||||
|
||||
private static getLookbackStartDate(): Date {
|
||||
return OneUptimeDate.addRemoveDays(
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
-TelemetryAttributeService.LOOKBACK_WINDOW_IN_DAYS,
|
||||
);
|
||||
}
|
||||
|
||||
private static async getCachedAttributes(
|
||||
cacheKey: string,
|
||||
): Promise<TelemetryAttributesCacheEntry | null> {
|
||||
let payload: JSONObject | null = null;
|
||||
|
||||
try {
|
||||
payload = await GlobalCache.getJSONObject(
|
||||
TelemetryAttributeService.CACHE_NAMESPACE,
|
||||
cacheKey,
|
||||
);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!payload) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const attributesValue: JSONObject["attributes"] = payload["attributes"];
|
||||
const refreshedAtValue: JSONObject["refreshedAt"] = payload["refreshedAt"];
|
||||
|
||||
if (
|
||||
!Array.isArray(attributesValue) ||
|
||||
typeof refreshedAtValue !== "string"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const attributeCandidates: Array<unknown> =
|
||||
attributesValue as Array<unknown>;
|
||||
|
||||
const attributes: Array<string> = attributeCandidates.filter(
|
||||
(attribute: unknown): attribute is string => {
|
||||
return typeof attribute === "string";
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
attributes,
|
||||
refreshedAt: OneUptimeDate.fromString(refreshedAtValue),
|
||||
};
|
||||
}
|
||||
|
||||
private static isCacheFresh(
|
||||
cacheEntry: TelemetryAttributesCacheEntry,
|
||||
): boolean {
|
||||
const now: Date = OneUptimeDate.getCurrentDate();
|
||||
const minutesSinceRefresh: number = Math.abs(
|
||||
OneUptimeDate.getNumberOfMinutesBetweenDates(cacheEntry.refreshedAt, now),
|
||||
);
|
||||
|
||||
return (
|
||||
minutesSinceRefresh <= TelemetryAttributeService.CACHE_STALE_AFTER_MINUTES
|
||||
);
|
||||
}
|
||||
|
||||
private static async storeAttributesInCache(
|
||||
cacheKey: string,
|
||||
attributes: Array<string>,
|
||||
): Promise<void> {
|
||||
const payload: JSONObject = {
|
||||
attributes,
|
||||
refreshedAt: OneUptimeDate.getCurrentDate().toISOString(),
|
||||
};
|
||||
|
||||
try {
|
||||
await GlobalCache.setJSON(
|
||||
TelemetryAttributeService.CACHE_NAMESPACE,
|
||||
cacheKey,
|
||||
payload,
|
||||
{
|
||||
expiresInSeconds:
|
||||
TelemetryAttributeService.CACHE_STALE_AFTER_MINUTES * 60,
|
||||
},
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static buildAttributesStatement(data: {
|
||||
projectId: ObjectID;
|
||||
telemetryType: TelemetryType;
|
||||
attributes: string[];
|
||||
}): Promise<void> {
|
||||
const { projectId, telemetryType, attributes } = data;
|
||||
tableName: string;
|
||||
attributesColumn: string;
|
||||
attributeKeysColumn: string;
|
||||
timeColumn: string;
|
||||
}): Statement {
|
||||
const lookbackStartDate: Date =
|
||||
TelemetryAttributeService.getLookbackStartDate();
|
||||
|
||||
// delete existing attributes
|
||||
await this.deleteBy({
|
||||
query: {
|
||||
projectId,
|
||||
telemetryType,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
const statement: Statement = SQL`
|
||||
WITH filtered AS (
|
||||
SELECT arrayJoin(
|
||||
if(
|
||||
${data.attributeKeysColumn} IS NULL OR empty(${data.attributeKeysColumn}),
|
||||
JSONExtractKeys(${data.attributesColumn}),
|
||||
${data.attributeKeysColumn}
|
||||
)
|
||||
) AS attribute
|
||||
FROM ${data.tableName}
|
||||
WHERE projectId = ${{
|
||||
type: TableColumnType.ObjectID,
|
||||
value: data.projectId,
|
||||
}}
|
||||
AND (
|
||||
${data.attributeKeysColumn} IS NOT NULL OR (
|
||||
${data.attributesColumn} IS NOT NULL AND
|
||||
${data.attributesColumn} != ''
|
||||
)
|
||||
)
|
||||
AND ${data.timeColumn} >= ${{
|
||||
type: TableColumnType.Date,
|
||||
value: lookbackStartDate,
|
||||
}}
|
||||
ORDER BY ${data.timeColumn} DESC
|
||||
LIMIT ${{
|
||||
type: TableColumnType.Number,
|
||||
value: TelemetryAttributeService.ROW_SCAN_LIMIT,
|
||||
}}
|
||||
)
|
||||
SELECT DISTINCT attribute
|
||||
FROM filtered
|
||||
WHERE attribute IS NOT NULL AND attribute != ''
|
||||
ORDER BY attribute ASC
|
||||
LIMIT ${{
|
||||
type: TableColumnType.Number,
|
||||
value: TelemetryAttributeService.ATTRIBUTES_LIMIT,
|
||||
}}
|
||||
`;
|
||||
|
||||
const telemetryAttribute: TelemetryAttribute = new TelemetryAttribute();
|
||||
return statement;
|
||||
}
|
||||
|
||||
telemetryAttribute.projectId = projectId;
|
||||
telemetryAttribute.telemetryType = telemetryType;
|
||||
telemetryAttribute.attributes = attributes;
|
||||
private static async fetchAttributesFromDatabase(data: {
|
||||
projectId: ObjectID;
|
||||
source: TelemetrySource;
|
||||
}): Promise<Array<string>> {
|
||||
const statement: Statement =
|
||||
TelemetryAttributeService.buildAttributesStatement({
|
||||
projectId: data.projectId,
|
||||
tableName: data.source.tableName,
|
||||
attributesColumn: data.source.attributesColumn,
|
||||
attributeKeysColumn: data.source.attributeKeysColumn,
|
||||
timeColumn: data.source.timeColumn,
|
||||
});
|
||||
|
||||
await this.create({
|
||||
data: telemetryAttribute,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
const dbResult: Results = await data.source.service.executeQuery(statement);
|
||||
const response: DbJSONResponse = await dbResult.json<{
|
||||
data?: Array<JSONObject>;
|
||||
}>();
|
||||
|
||||
const rows: Array<JSONObject> = response.data || [];
|
||||
|
||||
const attributeKeys: Array<string> = rows
|
||||
.map((row: JSONObject) => {
|
||||
const attribute: unknown = row["attribute"];
|
||||
return typeof attribute === "string" ? attribute.trim() : null;
|
||||
})
|
||||
.filter((attribute: string | null): attribute is string => {
|
||||
return Boolean(attribute);
|
||||
});
|
||||
|
||||
return Array.from(new Set(attributeKeys));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,33 @@
|
||||
import { MeteredPlanUtil } from "../Types/Billing/MeteredPlan/AllMeteredPlans";
|
||||
import TelemetryMeteredPlan from "../Types/Billing/MeteredPlan/TelemetryMeteredPlan";
|
||||
import QueryHelper from "../Types/Database/QueryHelper";
|
||||
import DatabaseService from "./DatabaseService";
|
||||
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
||||
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
||||
import LIMIT_MAX, { LIMIT_INFINITY } from "../../Types/Database/LimitMax";
|
||||
import OneUptimeDate from "../../Types/Date";
|
||||
import Decimal from "../../Types/Decimal";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import ProductType from "../../Types/MeteredPlan/ProductType";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import Model from "../../Models/DatabaseModels/TelemetryUsageBilling";
|
||||
import { IsBillingEnabled } from "../EnvironmentConfig";
|
||||
import Model, {
|
||||
DEFAULT_RETENTION_IN_DAYS,
|
||||
} from "../../Models/DatabaseModels/TelemetryUsageBilling";
|
||||
import TelemetryServiceService from "./TelemetryServiceService";
|
||||
import SpanService from "./SpanService";
|
||||
import LogService from "./LogService";
|
||||
import MetricService from "./MetricService";
|
||||
import ExceptionInstanceService from "./ExceptionInstanceService";
|
||||
import AnalyticsQueryHelper from "../Types/AnalyticsDatabase/QueryHelper";
|
||||
import DiskSize from "../../Types/DiskSize";
|
||||
import logger from "../Utils/Logger";
|
||||
import PositiveNumber from "../../Types/PositiveNumber";
|
||||
import TelemetryServiceModel from "../../Models/DatabaseModels/TelemetryService";
|
||||
import {
|
||||
AverageSpanRowSizeInBytes,
|
||||
AverageLogRowSizeInBytes,
|
||||
AverageMetricRowSizeInBytes,
|
||||
AverageExceptionRowSizeInBytes,
|
||||
IsBillingEnabled,
|
||||
} from "../EnvironmentConfig";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
@@ -31,9 +48,6 @@ export class Service extends DatabaseService<Model> {
|
||||
projectId: data.projectId,
|
||||
productType: data.productType,
|
||||
isReportedToBillingProvider: false,
|
||||
createdAt: QueryHelper.lessThan(
|
||||
OneUptimeDate.addRemoveDays(OneUptimeDate.getCurrentDate(), -1),
|
||||
), // we need to get everything that's not today.
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_MAX, /// because a project can have MANY telemetry services.
|
||||
@@ -47,6 +61,203 @@ export class Service extends DatabaseService<Model> {
|
||||
});
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async stageTelemetryUsageForProject(data: {
|
||||
projectId: ObjectID;
|
||||
productType: ProductType;
|
||||
usageDate?: Date;
|
||||
}): Promise<void> {
|
||||
if (!IsBillingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const usageDate: Date = data.usageDate
|
||||
? OneUptimeDate.fromString(data.usageDate)
|
||||
: OneUptimeDate.addRemoveDays(OneUptimeDate.getCurrentDate(), -1);
|
||||
|
||||
const averageRowSizeInBytes: number = this.getAverageRowSizeForProduct(
|
||||
data.productType,
|
||||
);
|
||||
const averageExceptionRowSizeInBytes: number =
|
||||
this.getAverageExceptionRowSize();
|
||||
|
||||
if (data.productType !== ProductType.Traces && averageRowSizeInBytes <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
data.productType === ProductType.Traces &&
|
||||
averageRowSizeInBytes <= 0 &&
|
||||
averageExceptionRowSizeInBytes <= 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const usageDayString: string = OneUptimeDate.getDateString(usageDate);
|
||||
const startOfDay: Date = OneUptimeDate.getStartOfDay(usageDate);
|
||||
const endOfDay: Date = OneUptimeDate.getEndOfDay(usageDate);
|
||||
|
||||
const telemetryServices: Array<TelemetryServiceModel> =
|
||||
await TelemetryServiceService.findBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
retainTelemetryDataForDays: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_MAX,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!telemetryServices || telemetryServices.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const telemetryService of telemetryServices) {
|
||||
if (!telemetryService?.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const existingEntry: Model | null = await this.findOneBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
productType: data.productType,
|
||||
telemetryServiceId: telemetryService.id,
|
||||
day: usageDayString,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingEntry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let estimatedBytes: number = 0;
|
||||
|
||||
try {
|
||||
if (data.productType === ProductType.Traces) {
|
||||
const spanCount: PositiveNumber = await SpanService.countBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
serviceId: telemetryService.id,
|
||||
startTime: AnalyticsQueryHelper.inBetween(startOfDay, endOfDay),
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_INFINITY,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const exceptionCount: PositiveNumber =
|
||||
await ExceptionInstanceService.countBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
serviceId: telemetryService.id,
|
||||
time: AnalyticsQueryHelper.inBetween(startOfDay, endOfDay),
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_INFINITY,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const totalSpanCount: number = spanCount.toNumber();
|
||||
const totalExceptionCount: number = exceptionCount.toNumber();
|
||||
|
||||
if (totalSpanCount <= 0 && totalExceptionCount <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
estimatedBytes =
|
||||
totalSpanCount * averageRowSizeInBytes +
|
||||
totalExceptionCount * averageExceptionRowSizeInBytes;
|
||||
} else if (data.productType === ProductType.Logs) {
|
||||
const count: PositiveNumber = await LogService.countBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
serviceId: telemetryService.id,
|
||||
time: AnalyticsQueryHelper.inBetween(startOfDay, endOfDay),
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_INFINITY,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const totalRowCount: number = count.toNumber();
|
||||
|
||||
if (totalRowCount <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
estimatedBytes = totalRowCount * averageRowSizeInBytes;
|
||||
} else if (data.productType === ProductType.Metrics) {
|
||||
const count: PositiveNumber = await MetricService.countBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
serviceId: telemetryService.id,
|
||||
time: AnalyticsQueryHelper.inBetween(startOfDay, endOfDay),
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_INFINITY,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const totalRowCount: number = count.toNumber();
|
||||
|
||||
if (totalRowCount <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
estimatedBytes = totalRowCount * averageRowSizeInBytes;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to compute telemetry usage for service ${telemetryService.id?.toString()}:`,
|
||||
);
|
||||
logger.error(error as Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (estimatedBytes <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const estimatedGigabytes: number = DiskSize.byteSizeToGB(estimatedBytes);
|
||||
|
||||
if (!Number.isFinite(estimatedGigabytes) || estimatedGigabytes <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const dataRetentionInDays: number =
|
||||
telemetryService.retainTelemetryDataForDays ||
|
||||
DEFAULT_RETENTION_IN_DAYS;
|
||||
|
||||
await this.updateUsageBilling({
|
||||
projectId: data.projectId,
|
||||
productType: data.productType,
|
||||
telemetryServiceId: telemetryService.id,
|
||||
dataIngestedInGB: estimatedGigabytes,
|
||||
retentionInDays: dataRetentionInDays,
|
||||
usageDate: usageDate,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public async updateUsageBilling(data: {
|
||||
projectId: ObjectID;
|
||||
@@ -54,6 +265,7 @@ export class Service extends DatabaseService<Model> {
|
||||
telemetryServiceId: ObjectID;
|
||||
dataIngestedInGB: number;
|
||||
retentionInDays: number;
|
||||
usageDate?: Date;
|
||||
}): Promise<void> {
|
||||
if (
|
||||
data.productType !== ProductType.Traces &&
|
||||
@@ -70,6 +282,12 @@ export class Service extends DatabaseService<Model> {
|
||||
data.productType,
|
||||
) as TelemetryMeteredPlan;
|
||||
|
||||
const usageDate: Date = data.usageDate
|
||||
? OneUptimeDate.fromString(data.usageDate)
|
||||
: OneUptimeDate.getCurrentDate();
|
||||
|
||||
const usageDayString: string = OneUptimeDate.getDateString(usageDate);
|
||||
|
||||
const totalCostOfThisOperationInUSD: number =
|
||||
serverMeteredPlan.getTotalCostInUSD({
|
||||
dataIngestedInGB: data.dataIngestedInGB,
|
||||
@@ -82,10 +300,7 @@ export class Service extends DatabaseService<Model> {
|
||||
productType: data.productType,
|
||||
telemetryServiceId: data.telemetryServiceId,
|
||||
isReportedToBillingProvider: false,
|
||||
createdAt: QueryHelper.inBetween(
|
||||
OneUptimeDate.addRemoveDays(OneUptimeDate.getCurrentDate(), -1),
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
),
|
||||
day: usageDayString,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
@@ -135,11 +350,9 @@ export class Service extends DatabaseService<Model> {
|
||||
usageBilling.telemetryServiceId = data.telemetryServiceId;
|
||||
usageBilling.retainTelemetryDataForDays = data.retentionInDays;
|
||||
usageBilling.isReportedToBillingProvider = false;
|
||||
usageBilling.createdAt = OneUptimeDate.getCurrentDate();
|
||||
usageBilling.createdAt = usageDate;
|
||||
|
||||
usageBilling.day = OneUptimeDate.getDateString(
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
);
|
||||
usageBilling.day = usageDayString;
|
||||
|
||||
usageBilling.totalCostInUSD = new Decimal(totalCostOfThisOperationInUSD);
|
||||
|
||||
@@ -151,6 +364,46 @@ export class Service extends DatabaseService<Model> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getAverageRowSizeForProduct(productType: ProductType): number {
|
||||
const fallbackSize: number = 1024;
|
||||
|
||||
// Narrow to telemetry product types before indexing to satisfy TypeScript
|
||||
if (
|
||||
productType !== ProductType.Traces &&
|
||||
productType !== ProductType.Logs &&
|
||||
productType !== ProductType.Metrics
|
||||
) {
|
||||
return fallbackSize;
|
||||
}
|
||||
|
||||
const value: number =
|
||||
{
|
||||
[ProductType.Traces]: AverageSpanRowSizeInBytes,
|
||||
[ProductType.Logs]: AverageLogRowSizeInBytes,
|
||||
[ProductType.Metrics]: AverageMetricRowSizeInBytes,
|
||||
}[productType] ?? fallbackSize;
|
||||
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
return fallbackSize;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private getAverageExceptionRowSize(): number {
|
||||
const fallbackSize: number = 1024;
|
||||
|
||||
if (!Number.isFinite(AverageExceptionRowSizeInBytes)) {
|
||||
return fallbackSize;
|
||||
}
|
||||
|
||||
if (AverageExceptionRowSizeInBytes <= 0) {
|
||||
return fallbackSize;
|
||||
}
|
||||
|
||||
return AverageExceptionRowSizeInBytes;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
||||
|
||||
@@ -5,6 +5,7 @@ import Model, {
|
||||
WorkspaceMiscData,
|
||||
} from "../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
||||
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
||||
import BadDataException from "../../Types/Exception/BadDataException";
|
||||
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
@@ -17,6 +18,14 @@ export class Service extends DatabaseService<Model> {
|
||||
projectId: ObjectID;
|
||||
workspaceType: WorkspaceType;
|
||||
}): Promise<Model | null> {
|
||||
if (!data.projectId) {
|
||||
throw new BadDataException("projectId is required");
|
||||
}
|
||||
|
||||
if (!data.workspaceType) {
|
||||
throw new BadDataException("workspaceType is required");
|
||||
}
|
||||
|
||||
return await this.findOneBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
@@ -38,6 +47,10 @@ export class Service extends DatabaseService<Model> {
|
||||
public async getProjectAuths(data: {
|
||||
projectId: ObjectID;
|
||||
}): Promise<Array<Model>> {
|
||||
if (!data.projectId) {
|
||||
throw new BadDataException("projectId is required");
|
||||
}
|
||||
|
||||
return await this.findBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
@@ -72,6 +85,26 @@ export class Service extends DatabaseService<Model> {
|
||||
workspaceProjectId: string;
|
||||
miscData: WorkspaceMiscData;
|
||||
}): Promise<void> {
|
||||
if (!data.projectId) {
|
||||
throw new BadDataException("projectId is required");
|
||||
}
|
||||
|
||||
if (!data.workspaceType) {
|
||||
throw new BadDataException("workspaceType is required");
|
||||
}
|
||||
|
||||
if (!data.authToken) {
|
||||
throw new BadDataException("authToken is required");
|
||||
}
|
||||
|
||||
if (!data.workspaceProjectId) {
|
||||
throw new BadDataException("workspaceProjectId is required");
|
||||
}
|
||||
|
||||
if (!data.miscData) {
|
||||
throw new BadDataException("miscData is required");
|
||||
}
|
||||
|
||||
let projectAuth: Model | null = await this.findOneBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
|
||||
@@ -56,6 +56,11 @@ export default class TelemetryMeteredPlan extends ServerMeteredPlan {
|
||||
): Promise<void> {
|
||||
// get all unreported logs
|
||||
|
||||
await TelemetryUsageBillingService.stageTelemetryUsageForProject({
|
||||
projectId: projectId,
|
||||
productType: this.productType,
|
||||
});
|
||||
|
||||
const usageBillings: Array<TelemetryUsageBilling> =
|
||||
await TelemetryUsageBillingService.getUnreportedUsageBilling({
|
||||
projectId: projectId,
|
||||
|
||||
25
Common/Server/Types/Database/FindAllBy.ts
Normal file
25
Common/Server/Types/Database/FindAllBy.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
||||
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
||||
import GroupBy from "./GroupBy";
|
||||
import Query from "./Query";
|
||||
import Select from "./Select";
|
||||
import Sort from "./Sort";
|
||||
import PositiveNumber from "../../../Types/PositiveNumber";
|
||||
|
||||
export default interface FindAllBy<TBaseModel extends BaseModel> {
|
||||
query: Query<TBaseModel>;
|
||||
select?: Select<TBaseModel> | undefined;
|
||||
sort?: Sort<TBaseModel> | undefined;
|
||||
groupBy?: GroupBy<TBaseModel> | undefined;
|
||||
props: DatabaseCommonInteractionProps;
|
||||
/**
|
||||
* Optional number of documents to skip before fetching results.
|
||||
* Acts the same way as `skip` in `findBy` but defaults to 0 when omitted.
|
||||
*/
|
||||
skip?: PositiveNumber | number | undefined;
|
||||
/**
|
||||
* Optional total number of documents to return across all batches.
|
||||
* When omitted, the method keeps fetching until no more data is returned.
|
||||
*/
|
||||
limit?: PositiveNumber | number | undefined;
|
||||
}
|
||||
@@ -177,11 +177,15 @@ export class Statement implements BaseQueryParams {
|
||||
};
|
||||
|
||||
if ((statementParam as StatementParameter).value instanceof Includes) {
|
||||
const isNumberArray: boolean = (
|
||||
const includesValues: Array<string | number | ObjectID> = (
|
||||
(statementParam as StatementParameter).value as Includes
|
||||
).values.every((v: string | number | ObjectID) => {
|
||||
return typeof v === "number";
|
||||
});
|
||||
).values as Array<string | number | ObjectID>;
|
||||
|
||||
const isNumberArray: boolean = includesValues.every(
|
||||
(v: string | number | ObjectID) => {
|
||||
return typeof v === "number";
|
||||
},
|
||||
);
|
||||
|
||||
if (isNumberArray) {
|
||||
return "Array(Int32)";
|
||||
|
||||
@@ -1,33 +1,26 @@
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import Execute from "../Execute";
|
||||
import LocalFile from "../LocalFile";
|
||||
import logger from "../Logger";
|
||||
import CaptureSpan from "../Telemetry/CaptureSpan";
|
||||
import CodeRepositoryFile from "./CodeRepositoryFile";
|
||||
import Dictionary from "../../../Types/Dictionary";
|
||||
import BadDataException from "../../../Types/Exception/BadDataException";
|
||||
|
||||
export default class CodeRepositoryUtil {
|
||||
@CaptureSpan()
|
||||
public static getCurrentCommitHash(data: {
|
||||
repoPath: string;
|
||||
}): Promise<string> {
|
||||
const command: string = `cd ${data.repoPath} && git rev-parse HEAD`;
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
return Execute.executeCommand(command);
|
||||
return this.runGitCommand(data.repoPath, ["rev-parse", "HEAD"]);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public static async addAllChangedFilesToGit(data: {
|
||||
repoPath: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git add -A`;
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
await this.runGitCommand(data.repoPath, ["add", "-A"]);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -36,26 +29,26 @@ export default class CodeRepositoryUtil {
|
||||
authorName: string;
|
||||
authorEmail: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git config --global user.name "${data.authorName}" && git config --global user.email "${data.authorEmail}"`;
|
||||
await this.runGitCommand(data.repoPath, [
|
||||
"config",
|
||||
"--global",
|
||||
"user.name",
|
||||
data.authorName,
|
||||
]);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
await this.runGitCommand(data.repoPath, [
|
||||
"config",
|
||||
"--global",
|
||||
"user.email",
|
||||
data.authorEmail,
|
||||
]);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public static async discardAllChangesOnCurrentBranch(data: {
|
||||
repoPath: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git checkout .`;
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
await this.runGitCommand(data.repoPath, ["checkout", "."]);
|
||||
}
|
||||
|
||||
// returns the folder name of the cloned repository
|
||||
@@ -64,33 +57,25 @@ export default class CodeRepositoryUtil {
|
||||
repoPath: string;
|
||||
repoUrl: string;
|
||||
}): Promise<string> {
|
||||
const command: string = `cd ${data.repoPath} && git clone ${data.repoUrl}`;
|
||||
await this.runGitCommand(data.repoPath, ["clone", data.repoUrl]);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
const normalizedUrl: string = data.repoUrl.trim().replace(/\/+$/g, "");
|
||||
const lastSegment: string =
|
||||
normalizedUrl.split("/").pop() || normalizedUrl.split(":").pop() || "";
|
||||
const folderName: string = lastSegment.replace(/\.git$/i, "");
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
|
||||
// get the folder name of the repository from the disk.
|
||||
|
||||
const getFolderNameCommand: string = `cd ${data.repoPath} && ls`;
|
||||
|
||||
const folderName: string =
|
||||
await Execute.executeCommand(getFolderNameCommand);
|
||||
if (!folderName) {
|
||||
throw new BadDataException(
|
||||
"Unable to determine repository folder name after cloning.",
|
||||
);
|
||||
}
|
||||
|
||||
return folderName.trim();
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
public static async pullChanges(data: { repoPath: string }): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git pull`;
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
await this.runGitCommand(data.repoPath, ["pull"]);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -98,13 +83,26 @@ export default class CodeRepositoryUtil {
|
||||
repoPath: string;
|
||||
branchName: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git checkout ${data.branchName} || git checkout -b ${data.branchName}`;
|
||||
try {
|
||||
await this.runGitCommand(data.repoPath, [
|
||||
"rev-parse",
|
||||
"--verify",
|
||||
data.branchName,
|
||||
]);
|
||||
await this.runGitCommand(data.repoPath, ["checkout", data.branchName]);
|
||||
} catch (error) {
|
||||
logger.debug(
|
||||
`Branch ${data.branchName} not found. Creating a new branch instead.`,
|
||||
);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
logger.debug(error);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
await this.runGitCommand(data.repoPath, [
|
||||
"checkout",
|
||||
"-b",
|
||||
data.branchName,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -112,15 +110,12 @@ export default class CodeRepositoryUtil {
|
||||
repoPath: string;
|
||||
filePath: string;
|
||||
}): Promise<string> {
|
||||
const path: string = LocalFile.sanitizeFilePath(
|
||||
`${data.repoPath}/${data.filePath}`,
|
||||
const absolutePath: string = this.resolvePathWithinRepo(
|
||||
data.repoPath,
|
||||
data.filePath,
|
||||
);
|
||||
|
||||
const command: string = `cat ${path}`;
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
return Execute.executeCommand(`${command}`);
|
||||
return LocalFile.read(absolutePath);
|
||||
}
|
||||
|
||||
// discard all changes in the working directory
|
||||
@@ -128,13 +123,7 @@ export default class CodeRepositoryUtil {
|
||||
public static async discardChanges(data: {
|
||||
repoPath: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git checkout .`;
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
await this.runGitCommand(data.repoPath, ["checkout", "."]);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -183,13 +172,9 @@ export default class CodeRepositoryUtil {
|
||||
`${data.repoPath}/${data.directoryPath}`,
|
||||
);
|
||||
|
||||
const command: string = `rm -rf ${totalPath}`;
|
||||
logger.debug("Deleting directory: " + totalPath);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
await LocalFile.deleteDirectory(totalPath);
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -197,11 +182,15 @@ export default class CodeRepositoryUtil {
|
||||
repoPath: string;
|
||||
branchName: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git checkout -b ${data.branchName}`;
|
||||
logger.debug(
|
||||
`Creating git branch '${data.branchName}' in ${path.resolve(data.repoPath)}`,
|
||||
);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
const stdout: string = await this.runGitCommand(data.repoPath, [
|
||||
"checkout",
|
||||
"-b",
|
||||
data.branchName,
|
||||
]);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
@@ -211,11 +200,14 @@ export default class CodeRepositoryUtil {
|
||||
repoPath: string;
|
||||
branchName: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git checkout ${data.branchName}`;
|
||||
logger.debug(
|
||||
`Checking out git branch '${data.branchName}' in ${path.resolve(data.repoPath)}`,
|
||||
);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
const stdout: string = await this.runGitCommand(data.repoPath, [
|
||||
"checkout",
|
||||
data.branchName,
|
||||
]);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
@@ -225,22 +217,51 @@ export default class CodeRepositoryUtil {
|
||||
repoPath: string;
|
||||
filePaths: Array<string>;
|
||||
}): Promise<void> {
|
||||
const filePaths: Array<string> = data.filePaths.map((filePath: string) => {
|
||||
if (filePath.startsWith("/")) {
|
||||
// remove the leading slash and return
|
||||
return filePath.substring(1);
|
||||
const repoRoot: string = path.resolve(data.repoPath);
|
||||
|
||||
const sanitizedRelativeFilePaths: Array<string> = [];
|
||||
|
||||
for (const inputFilePath of data.filePaths) {
|
||||
const normalizedPath: string = inputFilePath.startsWith("/")
|
||||
? inputFilePath.substring(1)
|
||||
: inputFilePath;
|
||||
|
||||
if (normalizedPath.trim() === "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
return filePath;
|
||||
});
|
||||
const absoluteFilePath: string = this.resolvePathWithinRepo(
|
||||
data.repoPath,
|
||||
normalizedPath,
|
||||
);
|
||||
|
||||
const command: string = `cd ${
|
||||
data.repoPath
|
||||
} && git add ${filePaths.join(" ")}`;
|
||||
const relativeFilePath: string = path.relative(
|
||||
repoRoot,
|
||||
absoluteFilePath,
|
||||
);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
if (relativeFilePath.trim() === "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
sanitizedRelativeFilePaths.push(
|
||||
LocalFile.sanitizeFilePath(relativeFilePath),
|
||||
);
|
||||
}
|
||||
|
||||
if (sanitizedRelativeFilePaths.length === 0) {
|
||||
logger.debug("git add skipped because no file paths were provided");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`Adding ${sanitizedRelativeFilePaths.length} file(s) to git in ${path.resolve(data.repoPath)}`,
|
||||
);
|
||||
|
||||
const stdout: string = await this.runGitCommand(data.repoPath, [
|
||||
"add",
|
||||
...sanitizedRelativeFilePaths,
|
||||
]);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
@@ -250,11 +271,13 @@ export default class CodeRepositoryUtil {
|
||||
repoPath: string;
|
||||
username: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git config user.name "${data.username}"`;
|
||||
logger.debug(`Setting git user.name in ${path.resolve(data.repoPath)}`);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
const stdout: string = await this.runGitCommand(data.repoPath, [
|
||||
"config",
|
||||
"user.name",
|
||||
data.username,
|
||||
]);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
@@ -264,11 +287,13 @@ export default class CodeRepositoryUtil {
|
||||
repoPath: string;
|
||||
message: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git commit -m "${data.message}"`;
|
||||
logger.debug("Executing git commit");
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
const stdout: string = await Execute.executeCommandFile({
|
||||
command: "git",
|
||||
args: ["commit", "-m", data.message],
|
||||
cwd: data.repoPath,
|
||||
});
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
@@ -288,15 +313,28 @@ export default class CodeRepositoryUtil {
|
||||
|
||||
const { repoPath, filePath } = data;
|
||||
|
||||
const command: string = `cd ${repoPath} && git log -1 --pretty=format:"%H" ".${filePath}"`;
|
||||
const repoRoot: string = path.resolve(repoPath);
|
||||
const absoluteTarget: string = this.resolvePathWithinRepo(
|
||||
repoPath,
|
||||
filePath,
|
||||
);
|
||||
const relativeTarget: string = path.relative(repoRoot, absoluteTarget);
|
||||
const gitArgument: string = LocalFile.sanitizeFilePath(
|
||||
`./${relativeTarget}`,
|
||||
);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
logger.debug(`Getting last commit hash for ${gitArgument} in ${repoRoot}`);
|
||||
|
||||
const hash: string = await Execute.executeCommand(command);
|
||||
const hash: string = await this.runGitCommand(repoRoot, [
|
||||
"log",
|
||||
"-1",
|
||||
"--pretty=format:%H",
|
||||
gitArgument,
|
||||
]);
|
||||
|
||||
logger.debug(hash);
|
||||
|
||||
return hash;
|
||||
return hash.trim();
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -318,11 +356,11 @@ export default class CodeRepositoryUtil {
|
||||
|
||||
totalPath = LocalFile.sanitizeFilePath(totalPath); // clean up the path
|
||||
|
||||
const output: string = await Execute.executeCommand(`ls ${totalPath}`);
|
||||
const entries: Array<fs.Dirent> = await LocalFile.readDirectory(totalPath);
|
||||
|
||||
const fileNames: Array<string> = output.split("\n");
|
||||
|
||||
return fileNames;
|
||||
return entries.map((entry: fs.Dirent) => {
|
||||
return entry.name;
|
||||
});
|
||||
}
|
||||
|
||||
@CaptureSpan()
|
||||
@@ -350,16 +388,12 @@ export default class CodeRepositoryUtil {
|
||||
totalPath = LocalFile.sanitizeFilePath(totalPath); // clean up the path
|
||||
|
||||
const files: Dictionary<CodeRepositoryFile> = {};
|
||||
const output: string = await Execute.executeCommand(`ls ${totalPath}`);
|
||||
|
||||
const fileNames: Array<string> = output.split("\n");
|
||||
|
||||
const subDirectories: Array<string> = [];
|
||||
|
||||
for (const fileName of fileNames) {
|
||||
if (fileName === "") {
|
||||
continue;
|
||||
}
|
||||
const entries: Array<fs.Dirent> = await LocalFile.readDirectory(totalPath);
|
||||
|
||||
for (const entry of entries) {
|
||||
const fileName: string = entry.name;
|
||||
|
||||
const filePath: string = LocalFile.sanitizeFilePath(
|
||||
`${directoryPath}/${fileName}`,
|
||||
@@ -369,13 +403,7 @@ export default class CodeRepositoryUtil {
|
||||
continue;
|
||||
}
|
||||
|
||||
const isDirectory: boolean = (
|
||||
await Execute.executeCommand(
|
||||
`file "${LocalFile.sanitizeFilePath(`${totalPath}/${fileName}`)}"`,
|
||||
)
|
||||
).includes("directory");
|
||||
|
||||
if (isDirectory) {
|
||||
if (entry.isDirectory()) {
|
||||
subDirectories.push(
|
||||
LocalFile.sanitizeFilePath(`${directoryPath}/${fileName}`),
|
||||
);
|
||||
@@ -449,4 +477,45 @@ export default class CodeRepositoryUtil {
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private static runGitCommand(
|
||||
repoPath: string,
|
||||
args: Array<string>,
|
||||
): Promise<string> {
|
||||
const cwd: string = path.resolve(repoPath);
|
||||
|
||||
logger.debug(
|
||||
`Executing git command in ${cwd}: git ${args
|
||||
.map((arg: string) => {
|
||||
return arg.includes(" ") ? `"${arg}"` : arg;
|
||||
})
|
||||
.join(" ")}`,
|
||||
);
|
||||
|
||||
return Execute.executeCommandFile({
|
||||
command: "git",
|
||||
args,
|
||||
cwd,
|
||||
});
|
||||
}
|
||||
|
||||
private static resolvePathWithinRepo(
|
||||
repoPath: string,
|
||||
targetPath: string,
|
||||
): string {
|
||||
const root: string = path.resolve(repoPath);
|
||||
const sanitizedTarget: string = LocalFile.sanitizeFilePath(
|
||||
targetPath,
|
||||
).replace(/^\/+/, "");
|
||||
const absoluteTarget: string = path.resolve(root, sanitizedTarget);
|
||||
|
||||
if (
|
||||
absoluteTarget !== root &&
|
||||
!absoluteTarget.startsWith(root + path.sep)
|
||||
) {
|
||||
throw new BadDataException("File path is outside the repository");
|
||||
}
|
||||
|
||||
return absoluteTarget;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,13 +170,15 @@ export default class GitHubUtil extends HostedCodeRepository {
|
||||
`https://github.com/${data.organizationName}/${data.repositoryName}.git`,
|
||||
);
|
||||
|
||||
const command: string = `git remote add ${
|
||||
data.remoteName
|
||||
} ${url.toString()}`;
|
||||
logger.debug(
|
||||
`Adding remote '${data.remoteName}' for ${data.organizationName}/${data.repositoryName}`,
|
||||
);
|
||||
|
||||
logger.debug("Executing command: " + command);
|
||||
|
||||
const result: string = await Execute.executeCommand(command);
|
||||
const result: string = await Execute.executeCommandFile({
|
||||
command: "git",
|
||||
args: ["remote", "add", data.remoteName, url.toString()],
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
|
||||
logger.debug(result);
|
||||
}
|
||||
@@ -197,10 +199,19 @@ export default class GitHubUtil extends HostedCodeRepository {
|
||||
"Pushing changes to remote repository with username: " + username,
|
||||
);
|
||||
|
||||
const command: string = `cd ${data.repoPath} && git push -u https://${username}:${password}@github.com/${data.organizationName}/${data.repositoryName}.git ${branchName}`;
|
||||
logger.debug("Executing command: " + command);
|
||||
const encodedUsername: string = encodeURIComponent(username);
|
||||
const encodedPassword: string = encodeURIComponent(password);
|
||||
const remoteUrl: string = `https://${encodedUsername}:${encodedPassword}@github.com/${data.organizationName}/${data.repositoryName}.git`;
|
||||
|
||||
const result: string = await Execute.executeCommand(command);
|
||||
logger.debug(
|
||||
`Pushing branch '${branchName}' to ${data.organizationName}/${data.repositoryName}`,
|
||||
);
|
||||
|
||||
const result: string = await Execute.executeCommandFile({
|
||||
command: "git",
|
||||
args: ["push", "-u", remoteUrl, branchName],
|
||||
cwd: data.repoPath,
|
||||
});
|
||||
|
||||
logger.debug(result);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user