mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 08:42:13 +02:00
Compare commits
47 Commits
sp-api
...
manual-wor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a18207b04f | ||
|
|
cf7c39d96c | ||
|
|
4738f8f930 | ||
|
|
390a4cbbf0 | ||
|
|
4bbbb9b473 | ||
|
|
482c7c142e | ||
|
|
9d581fc176 | ||
|
|
d680d09dc6 | ||
|
|
1ab55e1c19 | ||
|
|
427514ceb3 | ||
|
|
e443288074 | ||
|
|
d14686a288 | ||
|
|
f1b08dab84 | ||
|
|
74a73411ed | ||
|
|
ab76ecd04f | ||
|
|
5816be7041 | ||
|
|
300bbbc326 | ||
|
|
9c76bd283d | ||
|
|
cb263de054 | ||
|
|
06f3ba40dc | ||
|
|
f5febd029a | ||
|
|
09fde3a29b | ||
|
|
e628968b97 | ||
|
|
12b31ca237 | ||
|
|
f2de1695f5 | ||
|
|
ef8c36d176 | ||
|
|
2ea10bfbdd | ||
|
|
4dcd691ef7 | ||
|
|
61ff89af8f | ||
|
|
35b3dbfd8b | ||
|
|
9174a04795 | ||
|
|
4a80b1c8fd | ||
|
|
95c2bee9a9 | ||
|
|
3d0f0a62fe | ||
|
|
b23af6c6a9 | ||
|
|
434305acc9 | ||
|
|
a225314c65 | ||
|
|
a8e20574c7 | ||
|
|
c667fddc64 | ||
|
|
c2ce1ea140 | ||
|
|
8b1680fab5 | ||
|
|
fe64380384 | ||
|
|
449a1db573 | ||
|
|
e4c05c09f0 | ||
|
|
d4107e049f | ||
|
|
3811f9648c | ||
|
|
f4415dafc7 |
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
@@ -255,6 +255,21 @@ jobs:
|
||||
- name: build docker image
|
||||
run: sudo docker build -f ./ProbeIngest/Dockerfile .
|
||||
|
||||
docker-build-server-monitor-ingest:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Preinstall
|
||||
run: npm run prerun
|
||||
|
||||
# build image probe api
|
||||
- name: build docker image
|
||||
run: sudo docker build -f ./ServerMonitorIngest/Dockerfile .
|
||||
|
||||
docker-build-open-telemetry-ingest:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
|
||||
12
.github/workflows/compile.yml
vendored
12
.github/workflows/compile.yml
vendored
@@ -217,6 +217,18 @@ jobs:
|
||||
- run: cd Common && npm install
|
||||
- run: cd ProbeIngest && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-server-monitor-ingest:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- run: cd Common && npm install
|
||||
- run: cd ServerMonitorIngest && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-open-telemetry-ingest:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
|
||||
67
.github/workflows/release.yml
vendored
67
.github/workflows/release.yml
vendored
@@ -613,6 +613,69 @@ jobs:
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
server-monitor-ingest-docker-image-deploy:
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
oneuptime/server-monitor-ingest
|
||||
ghcr.io/oneuptime/server-monitor-ingest
|
||||
tags: |
|
||||
type=raw,value=release,enable=true
|
||||
type=semver,value=7.0.${{needs.generate-build-number.outputs.build_number}},pattern={{version}},enable=true
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Generate Dockerfile from Dockerfile.tpl
|
||||
run: npm run prerun
|
||||
|
||||
# Build and deploy probe-ingest.
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: ./ServerMonitorIngest/Dockerfile
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
|
||||
open-telemetry-ingest-docker-image-deploy:
|
||||
needs: [generate-build-number]
|
||||
runs-on: ubuntu-latest
|
||||
@@ -1590,7 +1653,7 @@ jobs:
|
||||
|
||||
test-e2e-release-saas:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [open-telemetry-ingest-docker-image-deploy, copilot-docker-image-deploy, fluent-ingest-docker-image-deploy, docs-docker-image-deploy, api-reference-docker-image-deploy, workflow-docker-image-deploy, llm-docker-image-deploy, accounts-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, haraka-docker-image-deploy, probe-ingest-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, worker-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, nginx-docker-image-deploy, incoming-request-ingest-docker-image-deploy]
|
||||
needs: [open-telemetry-ingest-docker-image-deploy, copilot-docker-image-deploy, fluent-ingest-docker-image-deploy, docs-docker-image-deploy, api-reference-docker-image-deploy, workflow-docker-image-deploy, llm-docker-image-deploy, accounts-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, haraka-docker-image-deploy, probe-ingest-docker-image-deploy, server-monitor-ingest-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, worker-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, nginx-docker-image-deploy, incoming-request-ingest-docker-image-deploy]
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
@@ -1643,7 +1706,7 @@ jobs:
|
||||
test-e2e-release-self-hosted:
|
||||
runs-on: ubuntu-latest
|
||||
# After all the jobs runs
|
||||
needs: [open-telemetry-ingest-docker-image-deploy, copilot-docker-image-deploy, incoming-request-ingest-docker-image-deploy, fluent-ingest-docker-image-deploy, docs-docker-image-deploy, api-reference-docker-image-deploy, workflow-docker-image-deploy, llm-docker-image-deploy, accounts-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, haraka-docker-image-deploy, probe-ingest-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, worker-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, nginx-docker-image-deploy]
|
||||
needs: [open-telemetry-ingest-docker-image-deploy, copilot-docker-image-deploy, incoming-request-ingest-docker-image-deploy, fluent-ingest-docker-image-deploy, docs-docker-image-deploy, api-reference-docker-image-deploy, workflow-docker-image-deploy, llm-docker-image-deploy, accounts-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, dashboard-docker-image-deploy, haraka-docker-image-deploy, probe-ingest-docker-image-deploy, server-monitor-ingest-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, worker-docker-image-deploy, otel-collector-docker-image-deploy, probe-docker-image-deploy, status-page-docker-image-deploy, test-docker-image-deploy, test-server-docker-image-deploy, publish-npm-packages, e2e-docker-image-deploy, helm-chart-deploy, generate-build-number, nginx-docker-image-deploy]
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
|
||||
68
.github/workflows/test-release.yaml
vendored
68
.github/workflows/test-release.yaml
vendored
@@ -664,6 +664,72 @@ jobs:
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
|
||||
server-monitor-ingest-docker-image-deploy:
|
||||
needs: generate-build-number
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Docker Meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
oneuptime/server-monitor-ingest
|
||||
ghcr.io/oneuptime/server-monitor-ingest
|
||||
tags: |
|
||||
type=raw,value=test,enable=true
|
||||
type=semver,value=7.0.${{needs.generate-build-number.outputs.build_number}}-test,pattern={{version}},enable=true
|
||||
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Generate Dockerfile from Dockerfile.tpl
|
||||
run: npm run prerun
|
||||
|
||||
# Build and deploy ServerMonitorIngest.
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
file: ./ServerMonitorIngest/Dockerfile
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
GIT_SHA=${{ github.sha }}
|
||||
APP_VERSION=7.0.${{needs.generate-build-number.outputs.build_number}}
|
||||
|
||||
|
||||
|
||||
|
||||
incoming-request-ingest-docker-image-deploy:
|
||||
needs: generate-build-number
|
||||
runs-on: ubuntu-latest
|
||||
@@ -1530,7 +1596,7 @@ jobs:
|
||||
|
||||
test-helm-chart:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [infrastructure-agent-deploy, llm-docker-image-deploy, open-telemetry-ingest-docker-image-deploy, copilot-docker-image-deploy, docs-docker-image-deploy, worker-docker-image-deploy, workflow-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, api-reference-docker-image-deploy, test-server-docker-image-deploy, test-docker-image-deploy, probe-ingest-docker-image-deploy, probe-docker-image-deploy, haraka-docker-image-deploy, dashboard-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, accounts-docker-image-deploy, otel-collector-docker-image-deploy, status-page-docker-image-deploy, nginx-docker-image-deploy, e2e-docker-image-deploy, fluent-ingest-docker-image-deploy, incoming-request-ingest-docker-image-deploy]
|
||||
needs: [infrastructure-agent-deploy, llm-docker-image-deploy, open-telemetry-ingest-docker-image-deploy, copilot-docker-image-deploy, docs-docker-image-deploy, worker-docker-image-deploy, workflow-docker-image-deploy, isolated-vm-docker-image-deploy, home-docker-image-deploy, api-reference-docker-image-deploy, test-server-docker-image-deploy, test-docker-image-deploy, probe-ingest-docker-image-deploy, server-monitor-ingest-docker-image-deploy, probe-docker-image-deploy, haraka-docker-image-deploy, dashboard-docker-image-deploy, admin-dashboard-docker-image-deploy, app-docker-image-deploy, accounts-docker-image-deploy, otel-collector-docker-image-deploy, status-page-docker-image-deploy, nginx-docker-image-deploy, e2e-docker-image-deploy, fluent-ingest-docker-image-deploy, incoming-request-ingest-docker-image-deploy]
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
|
||||
14
.vscode/launch.json
vendored
14
.vscode/launch.json
vendored
@@ -175,6 +175,20 @@
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/ServerMonitorIngest",
|
||||
"name": "ServerMonitorIngest: Debug with Docker",
|
||||
"port": 9941,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node",
|
||||
"restart": true,
|
||||
"autoAttachChildProcesses": true
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"localRoot": "${workspaceFolder}/IncomingRequestIngest",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.2-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.7.3-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.7.3-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.2-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -35,6 +35,7 @@ import fsp from "fs/promises";
|
||||
import Handlebars from "handlebars";
|
||||
import nodemailer, { Transporter } from "nodemailer";
|
||||
import Path from "path";
|
||||
import * as tls from "tls";
|
||||
|
||||
export default class MailService {
|
||||
public static isSMTPConfigValid(obj: JSONObject): boolean {
|
||||
@@ -204,10 +205,19 @@ export default class MailService {
|
||||
timeout?: number | undefined;
|
||||
},
|
||||
): Transporter {
|
||||
let tlsOptions: tls.ConnectionOptions | undefined = undefined;
|
||||
|
||||
if (!emailServer.secure) {
|
||||
tlsOptions = {
|
||||
rejectUnauthorized: false,
|
||||
};
|
||||
}
|
||||
|
||||
const privateMailer: Transporter = nodemailer.createTransport({
|
||||
host: emailServer.host.toString(),
|
||||
port: emailServer.port.toNumber(),
|
||||
secure: emailServer.secure,
|
||||
tls: tlsOptions,
|
||||
auth:
|
||||
emailServer.username && emailServer.password
|
||||
? {
|
||||
|
||||
@@ -1139,4 +1139,37 @@ export default class Incident extends BaseModel {
|
||||
nullable: true,
|
||||
})
|
||||
public postUpdatesToWorkspaceChannels?: Array<WorkspaceChannel> = 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({
|
||||
isDefaultValueColumn: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: "Should be visible on status page?",
|
||||
description: "Should this incident be visible on the status page?",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Boolean,
|
||||
default: true,
|
||||
nullable: true,
|
||||
})
|
||||
public isVisibleOnStatusPage?: boolean = undefined;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,12 @@ import Alert from "./Alert";
|
||||
@EnableDocumentation()
|
||||
@TenantColumn("projectId")
|
||||
@TableAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -58,7 +63,12 @@ import Alert from "./Alert";
|
||||
})
|
||||
export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -89,7 +99,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public project?: Project = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -114,7 +129,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public projectId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -146,7 +166,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public onCallDutyPolicy?: OnCallDutyPolicy = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -172,7 +197,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public onCallDutyPolicyId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -204,7 +234,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public triggeredByIncident?: Incident = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -228,7 +263,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public triggeredByIncidentId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -259,7 +299,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public triggeredByAlert?: Alert = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -283,7 +328,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public triggeredByAlertId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -307,7 +357,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public status?: OnCallDutyPolicyStatus = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -331,7 +386,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public statusMessage?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -355,7 +415,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public userNotificationEventType?: UserNotificationEventType = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -387,7 +452,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public createdByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -410,7 +480,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public createdByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@@ -437,7 +512,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public deletedByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -460,7 +540,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public deletedByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -493,7 +578,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public acknowledgedByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -516,7 +606,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public acknowledgedByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -535,7 +630,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public acknowledgedAt?: Date = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@@ -562,7 +662,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public acknowledgedByTeam?: Team = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -585,7 +690,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public acknowledgedByTeamId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@@ -604,7 +714,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public lastExecutedEscalationRuleOrder?: number = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@@ -623,7 +738,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public lastEscalationRuleExecutedAt?: Date = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -655,7 +775,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
@@ -680,7 +805,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public lastExecutedEscalationRuleId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@@ -700,7 +830,12 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
public executeNextEscalationRuleInMinutes?: number = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@@ -719,4 +854,67 @@ export default class OnCallDutyPolicyExecutionLog extends BaseModel {
|
||||
default: 1,
|
||||
})
|
||||
public onCallPolicyExecutionRepeatCount?: number = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
manyToOneRelationColumn: "triggeredByUserId",
|
||||
type: TableColumnType.Entity,
|
||||
modelType: User,
|
||||
title: "Triggered by User",
|
||||
description: "Relation to User who triggered on-clal policy",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "SET NULL",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "triggeredByUserId" })
|
||||
public triggeredByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectOnCallDutyPolicyExecutionLog,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
title: "Triggered by User ID",
|
||||
description: "User ID who triggered this on-call policy",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public triggeredByUserId?: ObjectID = undefined;
|
||||
}
|
||||
|
||||
@@ -998,4 +998,37 @@ export default class ScheduledMaintenance extends BaseModel {
|
||||
nullable: true,
|
||||
})
|
||||
public postUpdatesToWorkspaceChannels?: Array<WorkspaceChannel> = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectScheduledMaintenance,
|
||||
],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectScheduledMaintenance,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectScheduledMaintenance,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
isDefaultValueColumn: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: "Should be visible on status page?",
|
||||
description: "Should this incident be visible on the status page?",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Boolean,
|
||||
default: true,
|
||||
nullable: true,
|
||||
})
|
||||
public isVisibleOnStatusPage?: boolean = undefined;
|
||||
}
|
||||
|
||||
@@ -2006,4 +2006,118 @@ export default class StatusPage extends BaseModel {
|
||||
type: ColumnType.VeryLongText,
|
||||
})
|
||||
public subscriberEmailNotificationFooterText?: 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: "Show Incidents on Status Page",
|
||||
description: "Show Incidents on Status Page?",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Boolean,
|
||||
default: true,
|
||||
nullable: false,
|
||||
})
|
||||
@ColumnBillingAccessControl({
|
||||
read: PlanType.Free,
|
||||
update: PlanType.Growth,
|
||||
create: PlanType.Free,
|
||||
})
|
||||
public showIncidentsOnStatusPage?: 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,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
isDefaultValueColumn: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: "Show Announcements on Status Page",
|
||||
description: "Show Announcements on Status Page?",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Boolean,
|
||||
default: true,
|
||||
nullable: false,
|
||||
})
|
||||
@ColumnBillingAccessControl({
|
||||
read: PlanType.Free,
|
||||
update: PlanType.Growth,
|
||||
create: PlanType.Free,
|
||||
})
|
||||
public showAnnouncementsOnStatusPage?: 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,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
isDefaultValueColumn: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: "Show Scheduled Maintenance Events on Status Page",
|
||||
description: "Show Scheduled Maintenance Events on Status Page?",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Boolean,
|
||||
default: true,
|
||||
nullable: false,
|
||||
})
|
||||
@ColumnBillingAccessControl({
|
||||
read: PlanType.Free,
|
||||
update: PlanType.Growth,
|
||||
create: PlanType.Free,
|
||||
})
|
||||
public showScheduledMaintenanceEventsOnStatusPage?: boolean = undefined;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1740164199817 implements MigrationInterface {
|
||||
public name = "MigrationName1740164199817";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "Incident" ADD "isVisibleOnStatusPage" boolean DEFAULT true`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "ScheduledMaintenance" ADD "isVisibleOnStatusPage" boolean DEFAULT true`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "ScheduledMaintenance" DROP COLUMN "isVisibleOnStatusPage"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "Incident" DROP COLUMN "isVisibleOnStatusPage"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1740419151825 implements MigrationInterface {
|
||||
public name = "MigrationName1740419151825";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" ADD "showIncidentsOnStatusPage" boolean NOT NULL DEFAULT true`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" ADD "showAnnouncementsOnStatusPage" boolean NOT NULL DEFAULT true`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" ADD "showScheduledMaintenanceEventsOnStatusPage" boolean NOT NULL DEFAULT true`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" DROP COLUMN "showScheduledMaintenanceEventsOnStatusPage"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" DROP COLUMN "showAnnouncementsOnStatusPage"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPage" DROP COLUMN "showIncidentsOnStatusPage"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1740430229844 implements MigrationInterface {
|
||||
public name = "MigrationName1740430229844";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "OnCallDutyPolicyExecutionLog" ADD "triggeredByUserId" uuid`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "OnCallDutyPolicyExecutionLog" ADD CONSTRAINT "FK_0ed55adc637e8ed7a524f942b18" FOREIGN KEY ("triggeredByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "OnCallDutyPolicyExecutionLog" DROP CONSTRAINT "FK_0ed55adc637e8ed7a524f942b18"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "OnCallDutyPolicyExecutionLog" DROP COLUMN "triggeredByUserId"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,9 @@ import { MigrationName1739217257089 } from "./1739217257089-MigrationName";
|
||||
import { MigrationName1739282331053 } from "./1739282331053-MigrationName";
|
||||
import { MigrationName1739374537088 } from "./1739374537088-MigrationName";
|
||||
import { MigrationName1739569321582 } from "./1739569321582-MigrationName";
|
||||
import { MigrationName1740164199817 } from "./1740164199817-MigrationName";
|
||||
import { MigrationName1740419151825 } from "./1740419151825-MigrationName";
|
||||
import { MigrationName1740430229844 } from "./1740430229844-MigrationName";
|
||||
|
||||
export default [
|
||||
InitialMigration,
|
||||
@@ -214,4 +217,7 @@ export default [
|
||||
MigrationName1739282331053,
|
||||
MigrationName1739374537088,
|
||||
MigrationName1739569321582,
|
||||
MigrationName1740164199817,
|
||||
MigrationName1740419151825,
|
||||
MigrationName1740430229844,
|
||||
];
|
||||
|
||||
@@ -475,31 +475,31 @@ ${createdItem.remediationNotes || "No remediation notes provided."}`,
|
||||
}
|
||||
}
|
||||
|
||||
// // send message to workspaces - slack, teams, etc.
|
||||
// const createdChannels: {
|
||||
// channelsCreated: Array<WorkspaceChannel>;
|
||||
// } | null = await this.notifyWorkspaceOnIncidentCreate({
|
||||
// projectId: createdItem.projectId,
|
||||
// incidentId: createdItem.id!,
|
||||
// incidentNumber: createdItem.incidentNumber!,
|
||||
// });
|
||||
// send message to workspaces - slack, teams, etc.
|
||||
const createdChannels: {
|
||||
channelsCreated: Array<WorkspaceChannel>;
|
||||
} | null = await this.notifyWorkspaceOnIncidentCreate({
|
||||
projectId: createdItem.projectId,
|
||||
incidentId: createdItem.id!,
|
||||
incidentNumber: createdItem.incidentNumber!,
|
||||
});
|
||||
|
||||
// if (
|
||||
// createdChannels &&
|
||||
// createdChannels.channelsCreated &&
|
||||
// createdChannels.channelsCreated.length > 0
|
||||
// ) {
|
||||
// // update incident with these channels.
|
||||
// await this.updateOneById({
|
||||
// id: createdItem.id!,
|
||||
// data: {
|
||||
// postUpdatesToWorkspaceChannels: createdChannels.channelsCreated,
|
||||
// },
|
||||
// props: {
|
||||
// isRoot: true,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
if (
|
||||
createdChannels &&
|
||||
createdChannels.channelsCreated &&
|
||||
createdChannels.channelsCreated.length > 0
|
||||
) {
|
||||
// update incident with these channels.
|
||||
await this.updateOneById({
|
||||
id: createdItem.id!,
|
||||
data: {
|
||||
postUpdatesToWorkspaceChannels: createdChannels.channelsCreated,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return createdItem;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,14 @@ export class Service extends DatabaseService<Model> {
|
||||
createBy.data.status = OnCallDutyPolicyStatus.Scheduled;
|
||||
}
|
||||
|
||||
if (!createBy.data.statusMessage) {
|
||||
createBy.data.statusMessage = "Scheduled.";
|
||||
}
|
||||
|
||||
if (createBy.props.userId) {
|
||||
createBy.data.triggeredByUserId = createBy.props.userId;
|
||||
}
|
||||
|
||||
createBy.data.onCallPolicyExecutionRepeatCount = 1;
|
||||
|
||||
return { createBy, carryForward: null };
|
||||
|
||||
@@ -380,12 +380,11 @@ export class Service extends DatabaseService<StatusPage> {
|
||||
|
||||
public async getMonitorStatusTimelineForStatusPage(data: {
|
||||
monitorIds: Array<ObjectID>;
|
||||
historyDays: number;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
}): Promise<Array<MonitorStatusTimeline>> {
|
||||
const startDate: Date = OneUptimeDate.getSomeDaysAgo(
|
||||
data.historyDays || 14,
|
||||
);
|
||||
const endDate: Date = OneUptimeDate.getCurrentDate();
|
||||
const startDate: Date = data.startDate;
|
||||
const endDate: Date = data.endDate;
|
||||
|
||||
let monitorStatusTimelines: Array<MonitorStatusTimeline> = [];
|
||||
|
||||
@@ -756,9 +755,9 @@ export class Service extends DatabaseService<StatusPage> {
|
||||
|
||||
const numberOfDays: number = data.historyDays || 14;
|
||||
|
||||
const currentDate: Date = OneUptimeDate.getCurrentDate();
|
||||
const endDate: Date = OneUptimeDate.getCurrentDate();
|
||||
const startDate: Date = OneUptimeDate.getSomeDaysAgo(numberOfDays);
|
||||
const startAndEndDate: string = `${numberOfDays} days (${OneUptimeDate.getDateAsLocalFormattedString(startDate, true)} - ${OneUptimeDate.getDateAsLocalFormattedString(currentDate, true)})`;
|
||||
const startAndEndDate: string = `${numberOfDays} days (${OneUptimeDate.getDateAsLocalFormattedString(startDate, true)} - ${OneUptimeDate.getDateAsLocalFormattedString(endDate, true)})`;
|
||||
|
||||
if (statusPageResources.length === 0) {
|
||||
return {
|
||||
@@ -786,7 +785,8 @@ export class Service extends DatabaseService<StatusPage> {
|
||||
const timeline: Array<MonitorStatusTimeline> =
|
||||
await this.getMonitorStatusTimelineForStatusPage({
|
||||
monitorIds: monitors.monitorsOnStatusPage,
|
||||
historyDays: data.historyDays,
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
});
|
||||
|
||||
const reportItems: Array<StatusPageReportItem> = [];
|
||||
|
||||
@@ -29,8 +29,12 @@ import WorkspaceUserAuthTokenService from "./WorkspaceUserAuthTokenService";
|
||||
import WorkspaceMessagePayload, {
|
||||
WorkspaceMessageBlock,
|
||||
} from "../../Types/Workspace/WorkspaceMessagePayload";
|
||||
import WorkspaceProjectAuthToken from "../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
||||
import WorkspaceProjectAuthToken, {
|
||||
MiscData,
|
||||
SlackMiscData,
|
||||
} from "../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
||||
import WorkspaceProjectAuthTokenService from "./WorkspaceProjectAuthTokenService";
|
||||
import logger from "../Utils/Logger";
|
||||
|
||||
export interface NotificationFor {
|
||||
incidentId?: ObjectID | undefined;
|
||||
@@ -44,6 +48,31 @@ export class Service extends DatabaseService<Model> {
|
||||
super(Model);
|
||||
}
|
||||
|
||||
public getBotUserIdFromprojectAuthToken(data: {
|
||||
projectAuthToken: WorkspaceProjectAuthToken;
|
||||
workspaceType: WorkspaceType;
|
||||
}): string {
|
||||
const miscData: MiscData | undefined = data.projectAuthToken.miscData;
|
||||
|
||||
if (!miscData) {
|
||||
throw new BadDataException("Misc data not found in project auth token");
|
||||
}
|
||||
|
||||
if (data.workspaceType === WorkspaceType.Slack) {
|
||||
const userId: string = (miscData as SlackMiscData).botUserId;
|
||||
|
||||
if (!userId) {
|
||||
throw new BadDataException(
|
||||
"Bot user ID not found in project auth token",
|
||||
);
|
||||
}
|
||||
|
||||
return userId;
|
||||
}
|
||||
|
||||
throw new BadDataException("Workspace type not supported");
|
||||
}
|
||||
|
||||
public async createInviteAndPostToChannelsBasedOnRules(data: {
|
||||
projectId: ObjectID;
|
||||
notificationRuleEventType: NotificationRuleEventType;
|
||||
@@ -53,6 +82,11 @@ export class Service extends DatabaseService<Model> {
|
||||
}): Promise<{
|
||||
channelsCreated: Array<WorkspaceChannel>;
|
||||
} | null> {
|
||||
logger.debug(
|
||||
"WorkspaceNotificationRuleService.createInviteAndPostToChannelsBasedOnRules",
|
||||
);
|
||||
logger.debug(data);
|
||||
|
||||
const channelsCreated: Array<WorkspaceChannel> = [];
|
||||
|
||||
const projectAuths: Array<WorkspaceProjectAuthToken> =
|
||||
@@ -60,6 +94,9 @@ export class Service extends DatabaseService<Model> {
|
||||
projectId: data.projectId,
|
||||
});
|
||||
|
||||
logger.debug("projectAuths");
|
||||
logger.debug(projectAuths);
|
||||
|
||||
if (!projectAuths || projectAuths.length === 0) {
|
||||
// do nothing.
|
||||
return null;
|
||||
@@ -85,10 +122,14 @@ export class Service extends DatabaseService<Model> {
|
||||
notificationFor: data.notificationFor,
|
||||
});
|
||||
|
||||
logger.debug("notificationRules");
|
||||
logger.debug(notificationRules);
|
||||
|
||||
if (!notificationRules || notificationRules.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug("Creating channels based on rules");
|
||||
const createdWorkspaceChannels: Array<WorkspaceChannel> =
|
||||
await this.createChannelsBasedOnRules({
|
||||
projectOrUserAuthTokenForWorkspasce: authToken,
|
||||
@@ -100,6 +141,10 @@ export class Service extends DatabaseService<Model> {
|
||||
notificationEventType: data.notificationRuleEventType,
|
||||
});
|
||||
|
||||
logger.debug("createdWorkspaceChannels");
|
||||
logger.debug(createdWorkspaceChannels);
|
||||
|
||||
logger.debug("Inviting users and teams to channels based on rules");
|
||||
await this.inviteUsersAndTeamsToChannelsBasedOnRules({
|
||||
projectId: data.projectId,
|
||||
projectOrUserAuthTokenForWorkspasce: authToken,
|
||||
@@ -114,6 +159,7 @@ export class Service extends DatabaseService<Model> {
|
||||
),
|
||||
});
|
||||
|
||||
logger.debug("Getting existing channel names from notification rules");
|
||||
const existingChannelNames: Array<string> =
|
||||
this.getExistingChannelNamesFromNotificationRules({
|
||||
notificationRules: notificationRules.map((rule: Model) => {
|
||||
@@ -121,14 +167,25 @@ export class Service extends DatabaseService<Model> {
|
||||
}),
|
||||
}) || [];
|
||||
|
||||
// add created channel names to existing channel names.
|
||||
logger.debug("Existing channel names:");
|
||||
logger.debug(existingChannelNames);
|
||||
|
||||
logger.debug("Adding created channel names to existing channel names");
|
||||
for (const channel of createdWorkspaceChannels) {
|
||||
if (!existingChannelNames.includes(channel.name)) {
|
||||
existingChannelNames.push(channel.name);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Final list of channel names to post messages to:");
|
||||
logger.debug(existingChannelNames);
|
||||
|
||||
logger.debug("Posting messages to workspace channels");
|
||||
await this.postToWorkspaceChannels({
|
||||
workspaceUserId: this.getBotUserIdFromprojectAuthToken({
|
||||
projectAuthToken: projectAuth,
|
||||
workspaceType: workspaceType,
|
||||
}),
|
||||
projectOrUserAuthTokenForWorkspasce: authToken,
|
||||
workspaceType: workspaceType,
|
||||
workspaceMessagePayload: {
|
||||
@@ -138,23 +195,34 @@ export class Service extends DatabaseService<Model> {
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug("Channels created:");
|
||||
logger.debug(createdWorkspaceChannels);
|
||||
|
||||
channelsCreated.push(...createdWorkspaceChannels);
|
||||
}
|
||||
|
||||
logger.debug("Returning created channels");
|
||||
return {
|
||||
channelsCreated: channelsCreated,
|
||||
};
|
||||
}
|
||||
|
||||
public async postToWorkspaceChannels(data: {
|
||||
workspaceUserId: string;
|
||||
projectOrUserAuthTokenForWorkspasce: string;
|
||||
workspaceType: WorkspaceType;
|
||||
workspaceMessagePayload: WorkspaceMessagePayload;
|
||||
}): Promise<void> {
|
||||
logger.debug("postToWorkspaceChannels called with data:");
|
||||
logger.debug(data);
|
||||
|
||||
await WorkspaceUtil.getWorkspaceTypeUtil(data.workspaceType).sendMessage({
|
||||
userId: data.workspaceUserId,
|
||||
workspaceMessagePayload: data.workspaceMessagePayload,
|
||||
authToken: data.projectOrUserAuthTokenForWorkspasce,
|
||||
});
|
||||
|
||||
logger.debug("Message posted to workspace channels successfully");
|
||||
}
|
||||
|
||||
public async inviteUsersAndTeamsToChannelsBasedOnRules(data: {
|
||||
@@ -164,11 +232,17 @@ export class Service extends DatabaseService<Model> {
|
||||
notificationRules: Array<CreateChannelNotificationRule>;
|
||||
channelNames: Array<string>;
|
||||
}): Promise<void> {
|
||||
logger.debug("inviteUsersAndTeamsToChannelsBasedOnRules called with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const inviteUserIds: Array<ObjectID> =
|
||||
await this.getUsersIdsToInviteToChannel({
|
||||
notificationRules: data.notificationRules,
|
||||
});
|
||||
|
||||
logger.debug("User IDs to invite:");
|
||||
logger.debug(inviteUserIds);
|
||||
|
||||
const workspaceUserIds: Array<string> = [];
|
||||
|
||||
for (const userId of inviteUserIds) {
|
||||
@@ -176,7 +250,7 @@ export class Service extends DatabaseService<Model> {
|
||||
await this.getWorkspaceUserIdFromOneUptimeUserId({
|
||||
projectId: data.projectId,
|
||||
workspaceType: data.workspaceType,
|
||||
oneupitmeUserId: userId,
|
||||
oneuptimeUserId: userId,
|
||||
});
|
||||
|
||||
if (workspaceUserId) {
|
||||
@@ -184,6 +258,9 @@ export class Service extends DatabaseService<Model> {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Workspace User IDs to invite:");
|
||||
logger.debug(workspaceUserIds);
|
||||
|
||||
await WorkspaceUtil.getWorkspaceTypeUtil(
|
||||
data.workspaceType,
|
||||
).inviteUsersToChannels({
|
||||
@@ -193,19 +270,24 @@ export class Service extends DatabaseService<Model> {
|
||||
workspaceUserIds: workspaceUserIds,
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug("Users invited to channels successfully");
|
||||
}
|
||||
|
||||
public async getWorkspaceUserIdFromOneUptimeUserId(data: {
|
||||
projectId: ObjectID;
|
||||
workspaceType: WorkspaceType;
|
||||
oneupitmeUserId: ObjectID;
|
||||
oneuptimeUserId: ObjectID;
|
||||
}): Promise<string | null> {
|
||||
logger.debug("getWorkspaceUserIdFromOneUptimeUserId called with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const userAuth: WorkspaceUserAuthToken | null =
|
||||
await WorkspaceUserAuthTokenService.findOneBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
workspaceType: data.workspaceType,
|
||||
userId: data.oneupitmeUserId,
|
||||
userId: data.oneuptimeUserId,
|
||||
},
|
||||
select: {
|
||||
workspaceUserId: true,
|
||||
@@ -216,9 +298,13 @@ export class Service extends DatabaseService<Model> {
|
||||
});
|
||||
|
||||
if (!userAuth) {
|
||||
logger.debug("No userAuth found for given data");
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug("Found userAuth:");
|
||||
logger.debug(userAuth);
|
||||
|
||||
return userAuth.workspaceUserId?.toString() || null;
|
||||
}
|
||||
|
||||
@@ -229,6 +315,9 @@ export class Service extends DatabaseService<Model> {
|
||||
channelNameSiffix: string;
|
||||
notificationEventType: NotificationRuleEventType;
|
||||
}): Promise<Array<WorkspaceChannel>> {
|
||||
logger.debug("createChannelsBasedOnRules called with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const createdWorkspaceChannels: Array<WorkspaceChannel> = [];
|
||||
const createdChannelNames: Array<string> = [];
|
||||
|
||||
@@ -239,17 +328,23 @@ export class Service extends DatabaseService<Model> {
|
||||
notificationEventType: data.notificationEventType,
|
||||
});
|
||||
|
||||
logger.debug("New channel names to be created:");
|
||||
logger.debug(newChannelNames);
|
||||
|
||||
if (!newChannelNames || newChannelNames.length === 0) {
|
||||
logger.debug("No new channel names found. Returning empty array.");
|
||||
return [];
|
||||
}
|
||||
|
||||
for (const newChannelName of newChannelNames) {
|
||||
// if already created then skip it.
|
||||
if (createdChannelNames.includes(newChannelName)) {
|
||||
logger.debug(
|
||||
`Channel name ${newChannelName} already created. Skipping.`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// create channel.
|
||||
logger.debug(`Creating new channel with name: ${newChannelName}`);
|
||||
const channel: WorkspaceChannel =
|
||||
await WorkspaceUtil.getWorkspaceTypeUtil(
|
||||
data.workspaceType,
|
||||
@@ -258,17 +353,25 @@ export class Service extends DatabaseService<Model> {
|
||||
channelName: newChannelName,
|
||||
});
|
||||
|
||||
createdChannelNames.push(channel.name);
|
||||
logger.debug("Channel created:");
|
||||
logger.debug(channel);
|
||||
|
||||
createdChannelNames.push(channel.name);
|
||||
createdWorkspaceChannels.push(channel);
|
||||
}
|
||||
|
||||
logger.debug("Returning created workspace channels:");
|
||||
logger.debug(createdWorkspaceChannels);
|
||||
|
||||
return createdWorkspaceChannels;
|
||||
}
|
||||
|
||||
public async getUsersIdsToInviteToChannel(data: {
|
||||
notificationRules: Array<CreateChannelNotificationRule>;
|
||||
}): Promise<Array<ObjectID>> {
|
||||
logger.debug("getUsersIdsToInviteToChannel called with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const inviteUserIds: Array<ObjectID> = [];
|
||||
|
||||
for (const notificationRule of data.notificationRules) {
|
||||
@@ -282,6 +385,9 @@ export class Service extends DatabaseService<Model> {
|
||||
const userIds: Array<ObjectID> =
|
||||
workspaceRules.inviteUsersToNewChannel || [];
|
||||
|
||||
logger.debug("User IDs to invite from rule:");
|
||||
logger.debug(userIds);
|
||||
|
||||
for (const userId of userIds) {
|
||||
if (
|
||||
!inviteUserIds.find((id: ObjectID) => {
|
||||
@@ -304,9 +410,15 @@ export class Service extends DatabaseService<Model> {
|
||||
return new ObjectID(teamId.toString());
|
||||
});
|
||||
|
||||
logger.debug("Team IDs to invite from rule:");
|
||||
logger.debug(teamIds);
|
||||
|
||||
const usersInTeam: Array<User> =
|
||||
await TeamMemberService.getUsersInTeams(teamIds);
|
||||
|
||||
logger.debug("Users in teams:");
|
||||
logger.debug(usersInTeam);
|
||||
|
||||
for (const user of usersInTeam) {
|
||||
if (
|
||||
!inviteUserIds.find((id: ObjectID) => {
|
||||
@@ -323,12 +435,20 @@ export class Service extends DatabaseService<Model> {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Final list of user IDs to invite:");
|
||||
logger.debug(inviteUserIds);
|
||||
|
||||
return inviteUserIds;
|
||||
}
|
||||
|
||||
public getExistingChannelNamesFromNotificationRules(data: {
|
||||
notificationRules: Array<BaseNotificationRule>;
|
||||
}): Array<string> {
|
||||
logger.debug(
|
||||
"getExistingChannelNamesFromNotificationRules called with data:",
|
||||
);
|
||||
logger.debug(data);
|
||||
|
||||
const channelNames: Array<string> = [];
|
||||
|
||||
for (const notificationRule of data.notificationRules) {
|
||||
@@ -338,20 +458,25 @@ export class Service extends DatabaseService<Model> {
|
||||
const existingChannelNames: Array<string> =
|
||||
workspaceRules.existingChannelNames.split(",");
|
||||
|
||||
logger.debug("Existing channel names from rule:");
|
||||
logger.debug(existingChannelNames);
|
||||
|
||||
for (const channelName of existingChannelNames) {
|
||||
if (!channelName) {
|
||||
// if channel name is empty then skip it.
|
||||
logger.debug("Empty channel name found. Skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!channelNames.includes(channelName)) {
|
||||
// if channel name is not already added then add it.
|
||||
channelNames.push(channelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Final list of existing channel names:");
|
||||
logger.debug(channelNames);
|
||||
|
||||
return channelNames;
|
||||
}
|
||||
|
||||
@@ -360,11 +485,17 @@ export class Service extends DatabaseService<Model> {
|
||||
notificationRules: Array<CreateChannelNotificationRule>;
|
||||
channelNameSiffix: string;
|
||||
}): Array<string> {
|
||||
logger.debug("getNewChannelNamesFromNotificationRules called with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const channelNames: Array<string> = [];
|
||||
|
||||
for (const notificationRule of data.notificationRules) {
|
||||
const workspaceRules: CreateChannelNotificationRule = notificationRule;
|
||||
|
||||
logger.debug("Processing notification rule:");
|
||||
logger.debug(workspaceRules);
|
||||
|
||||
if (
|
||||
workspaceRules.shouldCreateNewChannel &&
|
||||
workspaceRules.newChannelTemplateName
|
||||
@@ -373,16 +504,30 @@ export class Service extends DatabaseService<Model> {
|
||||
workspaceRules.newChannelTemplateName ||
|
||||
`oneuptime-${data.notificationEventType.toLowerCase()}-`;
|
||||
|
||||
logger.debug("New channel template name:");
|
||||
logger.debug(newChannelName);
|
||||
|
||||
// add suffix and then check if it is already added or not.
|
||||
const channelName: string = newChannelName + data.channelNameSiffix;
|
||||
|
||||
logger.debug("Final channel name with suffix:");
|
||||
logger.debug(channelName);
|
||||
|
||||
if (!channelNames.includes(channelName)) {
|
||||
// if channel name is not already added then add it.
|
||||
channelNames.push(channelName);
|
||||
logger.debug(`Channel name ${channelName} added to the list.`);
|
||||
} else {
|
||||
logger.debug(
|
||||
`Channel name ${channelName} already exists in the list. Skipping.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Final list of new channel names:");
|
||||
logger.debug(channelNames);
|
||||
|
||||
return channelNames;
|
||||
}
|
||||
|
||||
@@ -391,7 +536,10 @@ export class Service extends DatabaseService<Model> {
|
||||
workspaceType: WorkspaceType;
|
||||
notificationRuleEventType: NotificationRuleEventType;
|
||||
}): Promise<Array<Model>> {
|
||||
return await this.findBy({
|
||||
logger.debug("getNotificationRules called with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const notificationRules: Array<Model> = await this.findBy({
|
||||
query: {
|
||||
projectId: data.projectId,
|
||||
workspaceType: data.workspaceType,
|
||||
@@ -406,6 +554,11 @@ export class Service extends DatabaseService<Model> {
|
||||
skip: 0,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
});
|
||||
|
||||
logger.debug("Notification rules retrieved:");
|
||||
logger.debug(notificationRules);
|
||||
|
||||
return notificationRules;
|
||||
}
|
||||
|
||||
private async getValuesBasedOnNotificationFor(data: {
|
||||
@@ -416,7 +569,13 @@ export class Service extends DatabaseService<Model> {
|
||||
| Array<string>
|
||||
| undefined;
|
||||
}> {
|
||||
logger.debug("getValuesBasedOnNotificationFor called with data:");
|
||||
logger.debug(data);
|
||||
|
||||
if (data.notificationFor.incidentId) {
|
||||
logger.debug("Fetching incident details for incident ID:");
|
||||
logger.debug(data.notificationFor.incidentId);
|
||||
|
||||
const incident: Incident | null = await IncidentService.findOneById({
|
||||
id: data.notificationFor.incidentId,
|
||||
select: {
|
||||
@@ -433,9 +592,14 @@ export class Service extends DatabaseService<Model> {
|
||||
});
|
||||
|
||||
if (!incident) {
|
||||
logger.debug("Incident not found for ID:");
|
||||
logger.debug(data.notificationFor.incidentId);
|
||||
throw new BadDataException("Incident ID not found");
|
||||
}
|
||||
|
||||
logger.debug("Incident details retrieved:");
|
||||
logger.debug(incident);
|
||||
|
||||
const monitorLabels: Array<Label> =
|
||||
await MonitorService.getLabelsForMonitors({
|
||||
monitorIds:
|
||||
@@ -444,6 +608,9 @@ export class Service extends DatabaseService<Model> {
|
||||
}) || [],
|
||||
});
|
||||
|
||||
logger.debug("Monitor labels retrieved:");
|
||||
logger.debug(monitorLabels);
|
||||
|
||||
return {
|
||||
[NotificationRuleConditionCheckOn.MonitorName]: undefined,
|
||||
[NotificationRuleConditionCheckOn.IncidentTitle]: incident.title || "",
|
||||
@@ -482,6 +649,9 @@ export class Service extends DatabaseService<Model> {
|
||||
}
|
||||
|
||||
if (data.notificationFor.alertId) {
|
||||
logger.debug("Fetching alert details for alert ID:");
|
||||
logger.debug(data.notificationFor.alertId);
|
||||
|
||||
const alert: Alert | null = await AlertService.findOneById({
|
||||
id: data.notificationFor.alertId,
|
||||
select: {
|
||||
@@ -498,14 +668,22 @@ export class Service extends DatabaseService<Model> {
|
||||
});
|
||||
|
||||
if (!alert) {
|
||||
logger.debug("Alert not found for ID:");
|
||||
logger.debug(data.notificationFor.alertId);
|
||||
throw new BadDataException("Alert ID not found");
|
||||
}
|
||||
|
||||
logger.debug("Alert details retrieved:");
|
||||
logger.debug(alert);
|
||||
|
||||
const monitorLabels: Array<Label> =
|
||||
await MonitorService.getLabelsForMonitors({
|
||||
monitorIds: alert?.monitor?.id ? [alert?.monitor?.id] : [],
|
||||
});
|
||||
|
||||
logger.debug("Monitor labels retrieved:");
|
||||
logger.debug(monitorLabels);
|
||||
|
||||
return {
|
||||
[NotificationRuleConditionCheckOn.MonitorName]: undefined,
|
||||
[NotificationRuleConditionCheckOn.IncidentTitle]: undefined,
|
||||
@@ -543,6 +721,9 @@ export class Service extends DatabaseService<Model> {
|
||||
}
|
||||
|
||||
if (data.notificationFor.scheduledMaintenanceId) {
|
||||
logger.debug("Fetching scheduled maintenance details for ID:");
|
||||
logger.debug(data.notificationFor.scheduledMaintenanceId);
|
||||
|
||||
const scheduledMaintenance: ScheduledMaintenance | null =
|
||||
await ScheduledMaintenanceService.findOneById({
|
||||
id: data.notificationFor.scheduledMaintenanceId,
|
||||
@@ -559,9 +740,14 @@ export class Service extends DatabaseService<Model> {
|
||||
});
|
||||
|
||||
if (!scheduledMaintenance) {
|
||||
logger.debug("Scheduled maintenance not found for ID:");
|
||||
logger.debug(data.notificationFor.scheduledMaintenanceId);
|
||||
throw new BadDataException("Scheduled Maintenance ID not found");
|
||||
}
|
||||
|
||||
logger.debug("Scheduled maintenance details retrieved:");
|
||||
logger.debug(scheduledMaintenance);
|
||||
|
||||
const monitorLabels: Array<Label> =
|
||||
await MonitorService.getLabelsForMonitors({
|
||||
monitorIds:
|
||||
@@ -572,6 +758,9 @@ export class Service extends DatabaseService<Model> {
|
||||
) || [],
|
||||
});
|
||||
|
||||
logger.debug("Monitor labels retrieved:");
|
||||
logger.debug(monitorLabels);
|
||||
|
||||
return {
|
||||
[NotificationRuleConditionCheckOn.MonitorName]: undefined,
|
||||
[NotificationRuleConditionCheckOn.IncidentTitle]: undefined,
|
||||
@@ -611,6 +800,9 @@ export class Service extends DatabaseService<Model> {
|
||||
}
|
||||
|
||||
if (data.notificationFor.monitorStatusTimelineId) {
|
||||
logger.debug("Fetching monitor status timeline details for ID:");
|
||||
logger.debug(data.notificationFor.monitorStatusTimelineId);
|
||||
|
||||
const monitorStatusTimeline: MonitorStatusTimeline | null =
|
||||
await MonitorStatusTimelineService.findOneById({
|
||||
id: data.notificationFor.monitorStatusTimelineId,
|
||||
@@ -628,6 +820,8 @@ export class Service extends DatabaseService<Model> {
|
||||
});
|
||||
|
||||
if (!monitorStatusTimeline) {
|
||||
logger.debug("Monitor status timeline not found for ID:");
|
||||
logger.debug(data.notificationFor.monitorStatusTimelineId);
|
||||
throw new BadDataException("Monitor Status Timeline ID not found");
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export class Service extends DatabaseService<Model> {
|
||||
authToken: true,
|
||||
workspaceProjectId: true,
|
||||
miscData: true,
|
||||
workspaceType: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
|
||||
@@ -222,6 +222,7 @@ export default class OnTriggerBaseModel<
|
||||
returnValues: {
|
||||
data: requestData,
|
||||
},
|
||||
isManualExecution: false,
|
||||
};
|
||||
|
||||
promises.push(props.executeWorkflow(executeWorkflow));
|
||||
|
||||
@@ -53,6 +53,7 @@ export default class WebhookTrigger extends TriggerCode {
|
||||
const executeWorkflow: ExecuteWorkflowType = {
|
||||
workflowId: new ObjectID(workflow._id!),
|
||||
returnValues: {},
|
||||
isManualExecution: false,
|
||||
};
|
||||
|
||||
if (
|
||||
@@ -122,6 +123,7 @@ export default class WebhookTrigger extends TriggerCode {
|
||||
const executeWorkflow: ExecuteWorkflowType = {
|
||||
workflowId: new ObjectID(workflow._id!),
|
||||
returnValues: {},
|
||||
isManualExecution: false,
|
||||
};
|
||||
|
||||
if (
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { ExpressRequest, ExpressResponse } from "../../../Utils/Express";
|
||||
import {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
NextFunction,
|
||||
} from "../../../Utils/Express";
|
||||
import Response from "../../../Utils/Response";
|
||||
import { RunOptions, RunReturnType } from "../ComponentCode";
|
||||
import TriggerCode, { ExecuteWorkflowType, InitProps } from "../TriggerCode";
|
||||
@@ -48,15 +52,23 @@ export default class WebhookTrigger extends TriggerCode {
|
||||
public override async init(props: InitProps): Promise<void> {
|
||||
props.router.get(
|
||||
`/trigger/:workflowId`,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
await this.initTrigger(req, res, props);
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
await this.initTrigger(req, res, props);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
props.router.post(
|
||||
`/trigger/:workflowId`,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
await this.initTrigger(req, res, props);
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
await this.initTrigger(req, res, props);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -77,6 +89,7 @@ export default class WebhookTrigger extends TriggerCode {
|
||||
"request-params": req.query,
|
||||
"request-body": req.body,
|
||||
},
|
||||
isManualExecution: false,
|
||||
};
|
||||
|
||||
await props.executeWorkflow(executeWorkflow);
|
||||
|
||||
@@ -10,6 +10,8 @@ import { Port } from "Common/Types/Workflow/Component";
|
||||
export interface ExecuteWorkflowType {
|
||||
workflowId: ObjectID;
|
||||
returnValues: JSONObject;
|
||||
// is this workflow triggered manually or not
|
||||
isManualExecution: boolean;
|
||||
}
|
||||
|
||||
export interface InitProps {
|
||||
|
||||
@@ -6,4 +6,5 @@ export interface RunProps {
|
||||
workflowId: ObjectID;
|
||||
workflowLogId: ObjectID | null;
|
||||
timeout: number;
|
||||
isManualExecution: boolean;
|
||||
}
|
||||
|
||||
@@ -15,125 +15,269 @@ import WorkspaceBase, { WorkspaceChannel } from "../WorkspaceBase";
|
||||
import WorkspaceType from "../../../../Types/Workspace/WorkspaceType";
|
||||
|
||||
export default class SlackUtil extends WorkspaceBase {
|
||||
public static override async inviteUserToChannel(data: {
|
||||
public static override async joinChannel(data: {
|
||||
authToken: string;
|
||||
channelName: string;
|
||||
channelId: string;
|
||||
}): Promise<void> {
|
||||
logger.debug("Joining channel with data:");
|
||||
logger.debug(data);
|
||||
|
||||
// Join channel
|
||||
const response = await API.post(
|
||||
URL.fromString("https://slack.com/api/conversations.join"),
|
||||
{
|
||||
channel: data.channelId,
|
||||
},
|
||||
{
|
||||
Authorization: `Bearer ${data.authToken}`,
|
||||
["Content-Type"]: "application/x-www-form-urlencoded",
|
||||
},
|
||||
);
|
||||
|
||||
logger.debug("Response from Slack API for joining channel:");
|
||||
logger.debug(response);
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
logger.error("Error response from Slack API:");
|
||||
logger.error(response);
|
||||
throw response;
|
||||
}
|
||||
|
||||
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
||||
logger.error("Invalid response from Slack API:");
|
||||
logger.error(response.jsonData);
|
||||
throw new BadRequestException("Invalid response");
|
||||
}
|
||||
|
||||
logger.debug("Channel joined successfully with data:");
|
||||
logger.debug(data);
|
||||
}
|
||||
|
||||
public static override async inviteUserToChannelByChannelId(data: {
|
||||
authToken: string;
|
||||
channelId: string;
|
||||
workspaceUserId: string;
|
||||
}): Promise<void> {
|
||||
const channelId: string = (
|
||||
await this.getWorkspaceChannelFromChannelId({
|
||||
authToken: data.authToken,
|
||||
channelId: data.channelName,
|
||||
})
|
||||
).id;
|
||||
logger.debug("Inviting user to channel with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post(
|
||||
URL.fromString("https://slack.com/api/conversations.invite"),
|
||||
{
|
||||
channel: channelId,
|
||||
channel: data.channelId,
|
||||
users: data.workspaceUserId,
|
||||
},
|
||||
{
|
||||
Authorization: `Bearer ${data.authToken}`,
|
||||
["Content-Type"]: "application/x-www-form-urlencoded",
|
||||
},
|
||||
);
|
||||
|
||||
logger.debug("Response from Slack API for inviting user:");
|
||||
logger.debug(response);
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
logger.error("Error response from Slack API:");
|
||||
logger.error(response);
|
||||
throw response;
|
||||
}
|
||||
|
||||
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
||||
logger.error("Invalid response from Slack API:");
|
||||
logger.error(response.jsonData);
|
||||
throw new BadRequestException("Invalid response");
|
||||
}
|
||||
|
||||
logger.debug("User invited to channel successfully.");
|
||||
}
|
||||
|
||||
public static override async inviteUserToChannelByChannelName(data: {
|
||||
authToken: string;
|
||||
channelName: string;
|
||||
workspaceUserId: string;
|
||||
}): Promise<void> {
|
||||
if (data.channelName && data.channelName.startsWith("#")) {
|
||||
// trim # from channel name
|
||||
data.channelName = data.channelName.substring(1);
|
||||
}
|
||||
|
||||
logger.debug("Inviting user to channel with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const channelId: string = (
|
||||
await this.getWorkspaceChannelFromChannelName({
|
||||
authToken: data.authToken,
|
||||
channelName: data.channelName,
|
||||
})
|
||||
).id;
|
||||
|
||||
return this.inviteUserToChannelByChannelId({
|
||||
authToken: data.authToken,
|
||||
channelId: channelId,
|
||||
workspaceUserId: data.workspaceUserId,
|
||||
});
|
||||
}
|
||||
|
||||
public static override async createChannelsIfDoesNotExist(data: {
|
||||
authToken: string;
|
||||
channelNames: Array<string>;
|
||||
}): Promise<Array<WorkspaceChannel>> {
|
||||
// check existing channels and only create if they dont exist.
|
||||
logger.debug("Creating channels if they do not exist with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const workspaceChannels: Array<WorkspaceChannel> = [];
|
||||
const existingWorkspaceChannels: Dictionary<WorkspaceChannel> =
|
||||
await this.getAllWorkspaceChannels({
|
||||
authToken: data.authToken,
|
||||
});
|
||||
|
||||
for (const channelName of data.channelNames) {
|
||||
logger.debug("Existing workspace channels:");
|
||||
logger.debug(existingWorkspaceChannels);
|
||||
|
||||
for (let channelName of data.channelNames) {
|
||||
// if channel name starts with #, remove it
|
||||
if (channelName && channelName.startsWith("#")) {
|
||||
channelName = channelName.substring(1);
|
||||
}
|
||||
|
||||
if (existingWorkspaceChannels[channelName]) {
|
||||
logger.debug(`Channel ${channelName} already exists.`);
|
||||
|
||||
workspaceChannels.push(existingWorkspaceChannels[channelName]!);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.debug(`Channel ${channelName} does not exist. Creating channel.`);
|
||||
const channel: WorkspaceChannel = await this.createChannel({
|
||||
authToken: data.authToken,
|
||||
channelName: channelName,
|
||||
});
|
||||
|
||||
if (channel) {
|
||||
logger.debug(`Channel ${channelName} created successfully.`);
|
||||
workspaceChannels.push(channel);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Channels created or found:");
|
||||
logger.debug(workspaceChannels);
|
||||
return workspaceChannels;
|
||||
}
|
||||
|
||||
public static override async getWorkspaceChannelFromChannelName(data: {
|
||||
authToken: string;
|
||||
channelName: string;
|
||||
}): Promise<WorkspaceChannel> {
|
||||
logger.debug("Getting workspace channel ID from channel name with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const channels: Dictionary<WorkspaceChannel> =
|
||||
await this.getAllWorkspaceChannels({
|
||||
authToken: data.authToken,
|
||||
});
|
||||
|
||||
logger.debug("All workspace channels:");
|
||||
logger.debug(channels);
|
||||
|
||||
if (!channels[data.channelName]) {
|
||||
logger.error("Channel not found.");
|
||||
throw new Error("Channel not found.");
|
||||
}
|
||||
|
||||
logger.debug("Workspace channel ID obtained:");
|
||||
logger.debug(channels[data.channelName]!.id);
|
||||
|
||||
return channels[data.channelName]!;
|
||||
}
|
||||
|
||||
public static override async getWorkspaceChannelFromChannelId(data: {
|
||||
authToken: string;
|
||||
channelId: string;
|
||||
}): Promise<WorkspaceChannel> {
|
||||
logger.debug("Getting workspace channel from channel ID with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.get<JSONObject>(
|
||||
await API.post<JSONObject>(
|
||||
URL.fromString("https://slack.com/api/conversations.info"),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${data.authToken}`,
|
||||
},
|
||||
params: {
|
||||
channel: data.channelId,
|
||||
},
|
||||
channel: data.channelId,
|
||||
},
|
||||
{
|
||||
Authorization: `Bearer ${data.authToken}`,
|
||||
["Content-Type"]: "application/x-www-form-urlencoded",
|
||||
},
|
||||
);
|
||||
|
||||
logger.debug("Response from Slack API for getting channel info:");
|
||||
logger.debug(response);
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
logger.error("Error response from Slack API:");
|
||||
logger.error(response);
|
||||
throw response;
|
||||
}
|
||||
|
||||
// check for ok response
|
||||
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
||||
logger.error("Invalid response from Slack API:");
|
||||
logger.error(response.jsonData);
|
||||
throw new BadRequestException("Invalid response");
|
||||
}
|
||||
|
||||
if (
|
||||
!((response.jsonData as JSONObject)?.["channel"] as JSONObject)?.["name"]
|
||||
) {
|
||||
logger.error("Invalid response from Slack API:");
|
||||
logger.error(response.jsonData);
|
||||
throw new Error("Invalid response");
|
||||
}
|
||||
|
||||
return {
|
||||
const channel: WorkspaceChannel = {
|
||||
name: ((response.jsonData as JSONObject)["channel"] as JSONObject)[
|
||||
"name"
|
||||
] as string,
|
||||
id: data.channelId,
|
||||
workspaceType: WorkspaceType.Slack,
|
||||
};
|
||||
|
||||
logger.debug("Workspace channel obtained:");
|
||||
logger.debug(channel);
|
||||
return channel;
|
||||
}
|
||||
|
||||
public static override async getAllWorkspaceChannels(data: {
|
||||
authToken: string;
|
||||
}): Promise<Dictionary<WorkspaceChannel>> {
|
||||
logger.debug("Getting all workspace channels with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.get<JSONObject>(
|
||||
await API.post<JSONObject>(
|
||||
URL.fromString("https://slack.com/api/conversations.list"),
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${data.authToken}`,
|
||||
},
|
||||
Authorization: `Bearer ${data.authToken}`,
|
||||
["Content-Type"]: "application/x-www-form-urlencoded",
|
||||
},
|
||||
);
|
||||
|
||||
logger.debug("Response from Slack API for getting all channels:");
|
||||
logger.debug(response);
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
logger.error("Error response from Slack API:");
|
||||
logger.error(response);
|
||||
throw response;
|
||||
}
|
||||
|
||||
// check for ok response
|
||||
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
||||
logger.error("Invalid response from Slack API:");
|
||||
logger.error(response.jsonData);
|
||||
throw new BadRequestException("Invalid response");
|
||||
}
|
||||
|
||||
const channels: Dictionary<WorkspaceChannel> = {};
|
||||
|
||||
for (const channel of (response.jsonData as JSONObject)[
|
||||
@@ -150,29 +294,42 @@ export default class SlackUtil extends WorkspaceBase {
|
||||
};
|
||||
}
|
||||
|
||||
logger.debug("All workspace channels obtained:");
|
||||
logger.debug(channels);
|
||||
return channels;
|
||||
}
|
||||
|
||||
public static override async sendMessage(data: {
|
||||
workspaceMessagePayload: WorkspaceMessagePayload;
|
||||
authToken: string; // which auth token should we use to send.
|
||||
userId: string;
|
||||
}): Promise<void> {
|
||||
logger.debug("Notify Slack");
|
||||
logger.debug("Sending message to Slack with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const blocks: Array<JSONObject> = this.getBlocksFromWorkspaceMessagePayload(
|
||||
data.workspaceMessagePayload,
|
||||
);
|
||||
|
||||
logger.debug("Blocks generated from workspace message payload:");
|
||||
logger.debug(blocks);
|
||||
|
||||
const existingWorkspaceChannels: Dictionary<WorkspaceChannel> =
|
||||
await this.getAllWorkspaceChannels({
|
||||
authToken: data.authToken,
|
||||
});
|
||||
|
||||
logger.debug("Existing workspace channels:");
|
||||
logger.debug(existingWorkspaceChannels);
|
||||
|
||||
const channelIdsToPostTo: Array<string> = [];
|
||||
|
||||
for (const channelName of data.workspaceMessagePayload.channelNames) {
|
||||
// get channel ids from existingWorkspaceChannels. IF channel doesn't exist, create it if createChannelsIfItDoesNotExist is true.
|
||||
for (let channelName of data.workspaceMessagePayload.channelNames) {
|
||||
if (channelName && channelName.startsWith("#")) {
|
||||
// trim # from channel name
|
||||
channelName = channelName.substring(1);
|
||||
}
|
||||
|
||||
let channel: WorkspaceChannel | null = null;
|
||||
|
||||
if (existingWorkspaceChannels[channelName]) {
|
||||
@@ -186,15 +343,35 @@ export default class SlackUtil extends WorkspaceBase {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Channel IDs to post to:");
|
||||
logger.debug(channelIdsToPostTo);
|
||||
|
||||
for (const channelId of channelIdsToPostTo) {
|
||||
try {
|
||||
// try catch here to prevent failure of one channel to prevent posting to other channels.
|
||||
// check if the user is in the channel.
|
||||
const isUserInChannel = await this.isUserInChannel({
|
||||
authToken: data.authToken,
|
||||
channelId: channelId,
|
||||
userId: data.userId,
|
||||
});
|
||||
|
||||
if (!isUserInChannel) {
|
||||
// add user to the channel
|
||||
await this.joinChannel({
|
||||
authToken: data.authToken,
|
||||
channelId: channelId,
|
||||
});
|
||||
}
|
||||
|
||||
await this.sendPayloadBlocksToChannel({
|
||||
authToken: data.authToken,
|
||||
channelId: channelId,
|
||||
blocks: blocks,
|
||||
});
|
||||
|
||||
logger.debug(`Message sent to channel ID ${channelId} successfully.`);
|
||||
} catch (e) {
|
||||
logger.error(`Error sending message to channel ID ${channelId}:`);
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
@@ -205,6 +382,9 @@ export default class SlackUtil extends WorkspaceBase {
|
||||
channelId: string;
|
||||
blocks: Array<JSONObject>;
|
||||
}): Promise<void> {
|
||||
logger.debug("Sending payload blocks to channel with data:");
|
||||
logger.debug(JSON.stringify(data, null, 2));
|
||||
|
||||
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post(
|
||||
URL.fromString("https://slack.com/api/chat.postMessage"),
|
||||
@@ -214,22 +394,35 @@ export default class SlackUtil extends WorkspaceBase {
|
||||
},
|
||||
{
|
||||
Authorization: `Bearer ${data.authToken}`,
|
||||
["Content-Type"]: "application/json",
|
||||
},
|
||||
);
|
||||
|
||||
logger.debug("Response from Slack API for sending message:");
|
||||
logger.debug(response);
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
logger.error("Error response from Slack API:");
|
||||
logger.error(response);
|
||||
throw response;
|
||||
}
|
||||
|
||||
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
||||
logger.error("Invalid response from Slack API:");
|
||||
logger.error(response.jsonData);
|
||||
throw new BadRequestException("Invalid response");
|
||||
}
|
||||
|
||||
logger.debug("Payload blocks sent to channel successfully.");
|
||||
}
|
||||
|
||||
public static override async createChannel(data: {
|
||||
authToken: string;
|
||||
channelName: string;
|
||||
}): Promise<WorkspaceChannel> {
|
||||
logger.debug("Creating channel with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const response: HTTPResponse<JSONObject> | HTTPErrorResponse =
|
||||
await API.post(
|
||||
URL.fromString("https://slack.com/api/conversations.create"),
|
||||
@@ -238,21 +431,36 @@ export default class SlackUtil extends WorkspaceBase {
|
||||
},
|
||||
{
|
||||
Authorization: `Bearer ${data.authToken}`,
|
||||
["Content-Type"]: "application/x-www-form-urlencoded",
|
||||
},
|
||||
);
|
||||
|
||||
logger.debug("Response from Slack API for creating channel:");
|
||||
logger.debug(response);
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
logger.error("Error response from Slack API:");
|
||||
logger.error(response);
|
||||
throw response;
|
||||
}
|
||||
|
||||
// check for ok response
|
||||
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
||||
logger.error("Invalid response from Slack API:");
|
||||
logger.error(response.jsonData);
|
||||
throw new BadRequestException("Invalid response");
|
||||
}
|
||||
|
||||
if (
|
||||
!((response.jsonData as JSONObject)?.["channel"] as JSONObject)?.["id"] ||
|
||||
!((response.jsonData as JSONObject)?.["channel"] as JSONObject)?.["name"]
|
||||
) {
|
||||
logger.error("Invalid response from Slack API:");
|
||||
logger.error(response.jsonData);
|
||||
throw new Error("Invalid response");
|
||||
}
|
||||
|
||||
return {
|
||||
const channel: WorkspaceChannel = {
|
||||
id: ((response.jsonData as JSONObject)["channel"] as JSONObject)[
|
||||
"id"
|
||||
] as string,
|
||||
@@ -261,36 +469,127 @@ export default class SlackUtil extends WorkspaceBase {
|
||||
] as string,
|
||||
workspaceType: WorkspaceType.Slack,
|
||||
};
|
||||
|
||||
logger.debug("Channel created successfully:");
|
||||
logger.debug(channel);
|
||||
return channel;
|
||||
}
|
||||
|
||||
public static override getHeaderBlock(data: {
|
||||
payloadHeaderBlock: WorkspacePayloadHeader;
|
||||
}): JSONObject {
|
||||
return {
|
||||
logger.debug("Getting header block with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const headerBlock: JSONObject = {
|
||||
type: "header",
|
||||
text: {
|
||||
type: "plain_text",
|
||||
text: data.payloadHeaderBlock.text,
|
||||
},
|
||||
};
|
||||
|
||||
logger.debug("Header block generated:");
|
||||
logger.debug(headerBlock);
|
||||
return headerBlock;
|
||||
}
|
||||
|
||||
public static override getMarkdownBlock(data: {
|
||||
payloadMarkdownBlock: WorkspacePayloadMarkdown;
|
||||
}): JSONObject {
|
||||
return {
|
||||
logger.debug("Getting markdown block with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const markdownBlock: JSONObject = {
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: data.payloadMarkdownBlock.text,
|
||||
},
|
||||
};
|
||||
|
||||
logger.debug("Markdown block generated:");
|
||||
logger.debug(markdownBlock);
|
||||
return markdownBlock;
|
||||
}
|
||||
|
||||
public static override async isUserInChannel(data: {
|
||||
authToken: string;
|
||||
channelId: string;
|
||||
userId: string;
|
||||
}): Promise<boolean> {
|
||||
const members: Array<string> = [];
|
||||
|
||||
logger.debug("Checking if user is in channel with data:");
|
||||
logger.debug(data);
|
||||
|
||||
let cursor: string | undefined = undefined;
|
||||
|
||||
do {
|
||||
// check if the user is in the channel, return true if they are, false if they are not
|
||||
|
||||
const requestBody: JSONObject = {
|
||||
channel: data.channelId,
|
||||
limit: 1000,
|
||||
};
|
||||
|
||||
if (cursor) {
|
||||
requestBody["cursor"] = cursor;
|
||||
}
|
||||
|
||||
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post<JSONObject>(
|
||||
URL.fromString("https://slack.com/api/conversations.members"),
|
||||
requestBody,
|
||||
{
|
||||
Authorization: `Bearer ${data.authToken}`,
|
||||
["Content-Type"]: "application/x-www-form-urlencoded",
|
||||
},
|
||||
);
|
||||
|
||||
logger.debug("Response from Slack API for getting channel members:");
|
||||
logger.debug(response);
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
logger.error("Error response from Slack API:");
|
||||
logger.error(response);
|
||||
throw response;
|
||||
}
|
||||
|
||||
// check for ok response
|
||||
|
||||
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
||||
logger.error("Invalid response from Slack API:");
|
||||
logger.error(response.jsonData);
|
||||
throw new BadRequestException("Invalid response");
|
||||
}
|
||||
|
||||
// check if the user is in the channel
|
||||
const membersOnThisPage: Array<string> = (
|
||||
response.jsonData as JSONObject
|
||||
)["members"] as Array<string>;
|
||||
|
||||
members.push(...membersOnThisPage);
|
||||
|
||||
cursor = (
|
||||
(response.jsonData as JSONObject)["response_metadata"] as JSONObject
|
||||
)?.["next_cursor"] as string;
|
||||
} while (cursor);
|
||||
|
||||
if (members.includes(data.userId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static override getButtonBlock(data: {
|
||||
payloadButtonBlock: WorkspaceMessagePayloadButton;
|
||||
}): JSONObject {
|
||||
return {
|
||||
logger.debug("Getting button block with data:");
|
||||
logger.debug(data);
|
||||
|
||||
const buttonBlock: JSONObject = {
|
||||
type: "button",
|
||||
text: {
|
||||
type: "plain_text",
|
||||
@@ -299,27 +598,34 @@ export default class SlackUtil extends WorkspaceBase {
|
||||
value: data.payloadButtonBlock.title,
|
||||
action_id: data.payloadButtonBlock.title,
|
||||
};
|
||||
|
||||
logger.debug("Button block generated:");
|
||||
logger.debug(buttonBlock);
|
||||
return buttonBlock;
|
||||
}
|
||||
|
||||
public static override async sendMessageToChannelViaIncomingWebhook(data: {
|
||||
url: URL;
|
||||
text: string;
|
||||
}): Promise<HTTPResponse<JSONObject> | HTTPErrorResponse> {
|
||||
let apiResult: HTTPResponse<JSONObject> | HTTPErrorResponse | null = null;
|
||||
logger.debug("Sending message to channel via incoming webhook with data:");
|
||||
logger.debug(data);
|
||||
|
||||
// https://api.slack.com/messaging/webhooks#advanced_message_formatting
|
||||
apiResult = await API.post(data.url, {
|
||||
blocks: [
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: `${data.text}`,
|
||||
const apiResult: HTTPResponse<JSONObject> | HTTPErrorResponse | null =
|
||||
await API.post(data.url, {
|
||||
blocks: [
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: `${data.text}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
],
|
||||
});
|
||||
|
||||
logger.debug("Response from Slack API for sending message via webhook:");
|
||||
logger.debug(apiResult);
|
||||
return apiResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,12 +45,17 @@
|
||||
],
|
||||
"scopes": {
|
||||
"user": [
|
||||
"identity.email",
|
||||
"identity.basic",
|
||||
"email"
|
||||
"users:read"
|
||||
],
|
||||
"bot": [
|
||||
"commands"
|
||||
"commands",
|
||||
"channels:history",
|
||||
"channels:join",
|
||||
"channels:manage",
|
||||
"channels:read",
|
||||
"channels:write.invites",
|
||||
"channels:write.topic",
|
||||
"chat:write"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -21,6 +21,13 @@ export interface WorkspaceChannel {
|
||||
}
|
||||
|
||||
export default class WorkspaceBase {
|
||||
public static async joinChannel(_data: {
|
||||
authToken: string;
|
||||
channelId: string;
|
||||
}): Promise<void> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static async sendPayloadBlocksToChannel(_data: {
|
||||
authToken: string;
|
||||
channelId: string;
|
||||
@@ -50,7 +57,7 @@ export default class WorkspaceBase {
|
||||
workspaceUserIds: Array<string>;
|
||||
}): Promise<void> {
|
||||
for (const userId of data.workspaceUserIds) {
|
||||
await this.inviteUserToChannel({
|
||||
await this.inviteUserToChannelByChannelName({
|
||||
authToken: data.authToken,
|
||||
channelName: data.channelName,
|
||||
workspaceUserId: userId,
|
||||
@@ -58,7 +65,7 @@ export default class WorkspaceBase {
|
||||
}
|
||||
}
|
||||
|
||||
public static async inviteUserToChannel(_data: {
|
||||
public static async inviteUserToChannelByChannelName(_data: {
|
||||
authToken: string;
|
||||
channelName: string;
|
||||
workspaceUserId: string;
|
||||
@@ -66,6 +73,12 @@ export default class WorkspaceBase {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static async inviteUserToChannelByChannelId(_data: {
|
||||
authToken: string;
|
||||
channelId: string;
|
||||
workspaceUserId: string;
|
||||
}): Promise<void> {}
|
||||
|
||||
public static async createChannelsIfDoesNotExist(_data: {
|
||||
authToken: string;
|
||||
channelNames: Array<string>;
|
||||
@@ -83,6 +96,7 @@ export default class WorkspaceBase {
|
||||
public static async sendMessage(_data: {
|
||||
workspaceMessagePayload: WorkspaceMessagePayload;
|
||||
authToken: string; // which auth token should we use to send.
|
||||
userId: string;
|
||||
}): Promise<void> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -93,6 +107,13 @@ export default class WorkspaceBase {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static async getWorkspaceChannelFromChannelName(_data: {
|
||||
authToken: string;
|
||||
channelName: string;
|
||||
}): Promise<WorkspaceChannel> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static async createChannel(_data: {
|
||||
authToken: string;
|
||||
channelName: string;
|
||||
@@ -166,4 +187,12 @@ export default class WorkspaceBase {
|
||||
}): Promise<HTTPResponse<JSONObject> | HTTPErrorResponse> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static async isUserInChannel(_data: {
|
||||
authToken: string;
|
||||
channelId: string;
|
||||
userId: string;
|
||||
}): Promise<boolean> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,26 +819,36 @@ export default class OneUptimeDate {
|
||||
return moment(date).isBefore(endDate);
|
||||
}
|
||||
|
||||
public static getCurrentDateAsFormattedString(): string {
|
||||
return this.getDateAsFormattedString(new Date());
|
||||
public static getCurrentDateAsFormattedString(options?: {
|
||||
onlyShowDate?: boolean;
|
||||
showSeconds?: boolean;
|
||||
}): string {
|
||||
return this.getDateAsFormattedString(new Date(), options);
|
||||
}
|
||||
|
||||
public static getDateAsFormattedString(
|
||||
date: string | Date,
|
||||
onlyShowDate?: boolean,
|
||||
options?: {
|
||||
onlyShowDate?: boolean;
|
||||
showSeconds?: boolean;
|
||||
},
|
||||
): string {
|
||||
date = this.fromString(date);
|
||||
|
||||
let formatstring: string = "MMM DD YYYY, HH:mm";
|
||||
|
||||
if (onlyShowDate) {
|
||||
if (options?.showSeconds) {
|
||||
formatstring = "MMM DD YYYY, HH:mm:ss";
|
||||
}
|
||||
|
||||
if (options?.onlyShowDate) {
|
||||
formatstring = "MMM DD, YYYY";
|
||||
}
|
||||
|
||||
return (
|
||||
moment(date).format(formatstring) +
|
||||
" " +
|
||||
(onlyShowDate ? "" : this.getCurrentTimezoneString())
|
||||
(options?.onlyShowDate ? "" : this.getCurrentTimezoneString())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -504,6 +504,7 @@ enum Permission {
|
||||
|
||||
ReadProjectOnCallDutyPolicyExecutionLogTimeline = "ReadProjectOnCallDutyPolicyExecutionLogTimeline",
|
||||
ReadProjectOnCallDutyPolicyExecutionLog = "ReadProjectOnCallDutyPolicyExecutionLog",
|
||||
CreateProjectOnCallDutyPolicyExecutionLog = "CreateProjectOnCallDutyPolicyExecutionLog",
|
||||
|
||||
// Resource Permissions (Team Permission)
|
||||
CreateProjectOnCallDutyPolicyEscalationRule = "CreateProjectOnCallDutyPolicyEscalationRule",
|
||||
@@ -2200,8 +2201,14 @@ export class PermissionHelper {
|
||||
{
|
||||
permission: Permission.ReadProjectOnCallDutyPolicyExecutionLog,
|
||||
title: "Read On-Call Duty Policy Execution Log",
|
||||
description:
|
||||
"This permission can read teams in on-call duty execution log.",
|
||||
description: "This permission can read on-call duty execution log.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CreateProjectOnCallDutyPolicyExecutionLog,
|
||||
title: "Create On-Call Duty Policy Execution Log",
|
||||
description: "This permission can create on-call duty execution log.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
@@ -90,6 +90,8 @@ export default interface ComponentMetadata {
|
||||
outPorts: Array<Port>;
|
||||
tableName?: string | undefined;
|
||||
documentationLink?: Route;
|
||||
// this is used in trigger component to show the manual execution button
|
||||
runWorkflowManuallyArguments?: Array<Argument> | undefined;
|
||||
}
|
||||
|
||||
export interface ComponentCategory {
|
||||
|
||||
@@ -16,7 +16,37 @@ const components: Array<ComponentMetadata> = [
|
||||
iconProp: IconProp.AltGlobe,
|
||||
componentType: ComponentType.Trigger,
|
||||
documentationLink: Route.fromString("/workflow/docs/Webhook.md"),
|
||||
arguments: [],
|
||||
|
||||
arguments: [
|
||||
|
||||
],
|
||||
runWorkflowManuallyArguments: [
|
||||
{
|
||||
id: "request-headers",
|
||||
name: "Request Headers",
|
||||
description: "Request Headers for this request",
|
||||
type: ComponentInputType.StringDictionary,
|
||||
required: false,
|
||||
placeholder: '{"header1": "value1", "header2": "value2", ....}',
|
||||
},
|
||||
{
|
||||
id: "request-params",
|
||||
name: "Request Query Params",
|
||||
description: "Request Query Params for this request",
|
||||
type: ComponentInputType.StringDictionary,
|
||||
required: false,
|
||||
placeholder: '{"query1": "value1", "query2": "value2", ....}',
|
||||
},
|
||||
{
|
||||
id: "request-body",
|
||||
name: "Request Body",
|
||||
description: "Request Body",
|
||||
type: ComponentInputType.JSON,
|
||||
required: false,
|
||||
placeholder: '{"key1": "value1", "key2": "value2", ....}',
|
||||
},
|
||||
],
|
||||
|
||||
returnValues: [
|
||||
{
|
||||
id: "request-headers",
|
||||
|
||||
@@ -9,6 +9,29 @@ import StatusPageGroup from "../../Models/DatabaseModels/StatusPageGroup";
|
||||
import UptimeUtil from "../Uptime/UptimeUtil";
|
||||
|
||||
export default class StatusPageResourceUptimeUtil {
|
||||
public static getWorstMonitorStatus(data: {
|
||||
monitorStatuses: Array<MonitorStatus>;
|
||||
}): MonitorStatus {
|
||||
let worstStatus: MonitorStatus = new MonitorStatus();
|
||||
worstStatus.name = "Operational";
|
||||
worstStatus.color = Green;
|
||||
|
||||
for (const status of data.monitorStatuses) {
|
||||
if (
|
||||
(worstStatus &&
|
||||
worstStatus.priority &&
|
||||
status.priority &&
|
||||
status.priority > worstStatus.priority) ||
|
||||
!worstStatus ||
|
||||
!worstStatus.priority
|
||||
) {
|
||||
worstStatus = status;
|
||||
}
|
||||
}
|
||||
|
||||
return worstStatus;
|
||||
}
|
||||
|
||||
public static getMonitorStatusTimelineForResource(data: {
|
||||
statusPageResource: StatusPageResource;
|
||||
monitorStatusTimelines: Array<MonitorStatusTimeline>;
|
||||
|
||||
@@ -355,4 +355,30 @@ export default class UptimeUtil {
|
||||
precision,
|
||||
});
|
||||
}
|
||||
|
||||
public static calculateAvgUptimePercentage(data: {
|
||||
uptimePercentages: Array<number>;
|
||||
precision: UptimePrecision;
|
||||
}): number {
|
||||
// calculate percentage.
|
||||
|
||||
const { uptimePercentages, precision } = data;
|
||||
|
||||
if (uptimePercentages.length === 0) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
let totalUptimePercentage: number = 0;
|
||||
|
||||
for (const uptimePercentage of uptimePercentages) {
|
||||
totalUptimePercentage += uptimePercentage;
|
||||
}
|
||||
|
||||
const percentage: number = totalUptimePercentage / uptimePercentages.length;
|
||||
|
||||
return this.roundToPrecision({
|
||||
number: percentage,
|
||||
precision,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.7.3-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -23,6 +23,9 @@ import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchem
|
||||
import { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import AlertInternalNote from "Common/Models/DatabaseModels/AlertInternalNote";
|
||||
import { ModalWidth } from "Common/UI/Components/Modal/Modal";
|
||||
import UserNotificationEventType from "Common/Types/UserNotification/UserNotificationEventType";
|
||||
import OnCallDutyPolicyExecutionLog from "Common/Models/DatabaseModels/OnCallDutyPolicyExecutionLog";
|
||||
import OnCallDutyPolicy from "Common/Models/DatabaseModels/OnCallDutyPolicy";
|
||||
|
||||
export interface ComponentProps {
|
||||
alertId: ObjectID;
|
||||
@@ -34,6 +37,8 @@ const AlertFeedElement: FunctionComponent<ComponentProps> = (
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||
const [error, setError] = React.useState<string | undefined>(undefined);
|
||||
const [feedItems, setFeedItems] = React.useState<FeedItemProps[]>([]);
|
||||
const [showOnCallPolicyModal, setShowOnCallPolicyModal] =
|
||||
React.useState<boolean>(false);
|
||||
|
||||
const [showPrivateNoteModal, setShowPrivateNoteModal] =
|
||||
React.useState<boolean>(false);
|
||||
@@ -189,6 +194,14 @@ const AlertFeedElement: FunctionComponent<ComponentProps> = (
|
||||
"This is the timeline and feed for this alert. You can see all the updates and information about this alert here."
|
||||
}
|
||||
buttons={[
|
||||
{
|
||||
title: "Execute On-Call Policy",
|
||||
buttonStyle: ButtonStyleType.NORMAL,
|
||||
icon: IconProp.Call,
|
||||
onClick: () => {
|
||||
setShowOnCallPolicyModal(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Add Private Note",
|
||||
buttonStyle: ButtonStyleType.NORMAL,
|
||||
@@ -217,6 +230,58 @@ const AlertFeedElement: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
)}
|
||||
|
||||
{showOnCallPolicyModal && (
|
||||
<ModelFormModal
|
||||
modelType={OnCallDutyPolicyExecutionLog}
|
||||
modalWidth={ModalWidth.Normal}
|
||||
name={"execute-on-call-policy"}
|
||||
title={"Execute On-Call Policy"}
|
||||
description={
|
||||
"Execute the on-call policy for this alert. This will notify the on-call team members and start the on-call process."
|
||||
}
|
||||
onClose={() => {
|
||||
setShowOnCallPolicyModal(false);
|
||||
}}
|
||||
submitButtonText="Execute Policy"
|
||||
onBeforeCreate={async (model: OnCallDutyPolicyExecutionLog) => {
|
||||
model.triggeredByAlertId = props.alertId!;
|
||||
model.userNotificationEventType =
|
||||
UserNotificationEventType.AlertCreated;
|
||||
return model;
|
||||
}}
|
||||
onSuccess={() => {
|
||||
setShowOnCallPolicyModal(false);
|
||||
fetchItems().catch((err: unknown) => {
|
||||
setError(API.getFriendlyMessage(err as Exception));
|
||||
});
|
||||
}}
|
||||
formProps={{
|
||||
name: "create-on-call-policy-log",
|
||||
modelType: OnCallDutyPolicyExecutionLog,
|
||||
id: "create-on-call-policy-log",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
onCallDutyPolicy: true,
|
||||
},
|
||||
title: "Select On-Call Policy",
|
||||
description:
|
||||
"Select the on-call policy to execute for this alert.",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
dropdownModal: {
|
||||
type: OnCallDutyPolicy,
|
||||
labelField: "name",
|
||||
valueField: "_id",
|
||||
},
|
||||
required: true,
|
||||
placeholder: "Select On-Call Policy",
|
||||
},
|
||||
],
|
||||
formType: FormType.Create,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showPrivateNoteModal && (
|
||||
<ModelFormModal
|
||||
modalWidth={ModalWidth.Large}
|
||||
|
||||
@@ -25,6 +25,9 @@ import { FormType } from "Common/UI/Components/Forms/ModelForm";
|
||||
import OneUptimeDate from "Common/Types/Date";
|
||||
import IncidentInternalNote from "Common/Models/DatabaseModels/IncidentInternalNote";
|
||||
import { ModalWidth } from "Common/UI/Components/Modal/Modal";
|
||||
import OnCallDutyPolicyExecutionLog from "Common/Models/DatabaseModels/OnCallDutyPolicyExecutionLog";
|
||||
import UserNotificationEventType from "Common/Types/UserNotification/UserNotificationEventType";
|
||||
import OnCallDutyPolicy from "Common/Models/DatabaseModels/OnCallDutyPolicy";
|
||||
|
||||
export interface ComponentProps {
|
||||
incidentId: ObjectID;
|
||||
@@ -36,6 +39,8 @@ const IncidentFeedElement: FunctionComponent<ComponentProps> = (
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||
const [error, setError] = React.useState<string | undefined>(undefined);
|
||||
const [feedItems, setFeedItems] = React.useState<FeedItemProps[]>([]);
|
||||
const [showOnCallPolicyModal, setShowOnCallPolicyModal] =
|
||||
React.useState<boolean>(false);
|
||||
|
||||
const [showPublicNoteModal, setShowPublicNoteModal] =
|
||||
React.useState<boolean>(false);
|
||||
@@ -230,6 +235,14 @@ const IncidentFeedElement: FunctionComponent<ComponentProps> = (
|
||||
"This is the timeline and feed for this incident. You can see all the updates and information about this incident here."
|
||||
}
|
||||
buttons={[
|
||||
{
|
||||
title: "Execute On-Call Policy",
|
||||
buttonStyle: ButtonStyleType.NORMAL,
|
||||
icon: IconProp.Call,
|
||||
onClick: () => {
|
||||
setShowOnCallPolicyModal(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Add Public Note",
|
||||
buttonStyle: ButtonStyleType.NORMAL,
|
||||
@@ -265,6 +278,59 @@ const IncidentFeedElement: FunctionComponent<ComponentProps> = (
|
||||
noItemsMessage="Looks like there are no items in this feed for this incident."
|
||||
/>
|
||||
)}
|
||||
|
||||
{showOnCallPolicyModal && (
|
||||
<ModelFormModal
|
||||
modelType={OnCallDutyPolicyExecutionLog}
|
||||
modalWidth={ModalWidth.Normal}
|
||||
name={"execute-on-call-policy"}
|
||||
title={"Execute On-Call Policy"}
|
||||
description={
|
||||
"Execute the on-call policy for this incident. This will notify the on-call team members and start the on-call process."
|
||||
}
|
||||
onClose={() => {
|
||||
setShowOnCallPolicyModal(false);
|
||||
}}
|
||||
submitButtonText="Execute Policy"
|
||||
onBeforeCreate={async (model: OnCallDutyPolicyExecutionLog) => {
|
||||
model.triggeredByIncidentId = props.incidentId!;
|
||||
model.userNotificationEventType =
|
||||
UserNotificationEventType.IncidentCreated;
|
||||
return model;
|
||||
}}
|
||||
onSuccess={() => {
|
||||
setShowOnCallPolicyModal(false);
|
||||
fetchItems().catch((err: unknown) => {
|
||||
setError(API.getFriendlyMessage(err as Exception));
|
||||
});
|
||||
}}
|
||||
formProps={{
|
||||
name: "create-on-call-policy-log",
|
||||
modelType: OnCallDutyPolicyExecutionLog,
|
||||
id: "create-on-call-policy-log",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
onCallDutyPolicy: true,
|
||||
},
|
||||
title: "Select On-Call Policy",
|
||||
description:
|
||||
"Select the on-call policy to execute for this incident.",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
dropdownModal: {
|
||||
type: OnCallDutyPolicy,
|
||||
labelField: "name",
|
||||
valueField: "_id",
|
||||
},
|
||||
required: true,
|
||||
placeholder: "Select On-Call Policy",
|
||||
},
|
||||
],
|
||||
formType: FormType.Create,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showPublicNoteModal && (
|
||||
<ModelFormModal
|
||||
modelType={IncidentPublicNote}
|
||||
|
||||
@@ -47,7 +47,9 @@ export interface ComponentProps {
|
||||
const MonitorsTable: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
let cardbuttons: Array<CardButtonSchema> = [];
|
||||
let cardbuttons: Array<CardButtonSchema> = props.cardButtons
|
||||
? [...props.cardButtons]
|
||||
: [];
|
||||
|
||||
if (!props.disableCreate) {
|
||||
// then add a card button that takes to monitor create page
|
||||
|
||||
@@ -25,6 +25,8 @@ import AlertView from "../../../Components/Alert/Alert";
|
||||
|
||||
export interface ComponentProps {
|
||||
onCallDutyPolicyId?: ObjectID | undefined; // if this is undefined. then it'll show logs for all policies.
|
||||
incidentId?: ObjectID | undefined;
|
||||
alertId?: ObjectID | undefined;
|
||||
}
|
||||
|
||||
const ExecutionLogsTable: FunctionComponent<ComponentProps> = (
|
||||
@@ -42,6 +44,14 @@ const ExecutionLogsTable: FunctionComponent<ComponentProps> = (
|
||||
query.onCallDutyPolicyId = props.onCallDutyPolicyId.toString();
|
||||
}
|
||||
|
||||
if (props.incidentId) {
|
||||
query.triggeredByIncidentId = props.incidentId;
|
||||
}
|
||||
|
||||
if (props.alertId) {
|
||||
query.triggeredByAlertId = props.alertId;
|
||||
}
|
||||
|
||||
let columns: Columns<OnCallDutyPolicyExecutionLog> = [];
|
||||
let filters: Array<Filter<OnCallDutyPolicyExecutionLog>> = [];
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ const NotificationRuleForm: FunctionComponent<ComponentProps> = (
|
||||
required: true,
|
||||
description: `If your new channel name is "oneuptime-${props.eventType.toLowerCase()}-", then we will append the ${props.eventType} number in the end so, it'll look like "oneuptime-${props.eventType.toLowerCase()}-X".`,
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: `oneupitme-${props.eventType.toLowerCase()}-`,
|
||||
placeholder: `oneuptime-${props.eventType.toLowerCase()}-`,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
|
||||
@@ -134,6 +134,15 @@ const NotificationRuleViewElement: FunctionComponent<ComponentProps> = (
|
||||
title: `${props.workspaceType} Channel Template Name`,
|
||||
description: `If your new channel name is "oneuptime-${props.eventType.toLowerCase()}-", then we will append the ${props.eventType} in the end so, it'll look like "oneuptime-${props.eventType.toLowerCase()}-X".`,
|
||||
fieldType: FieldType.Text,
|
||||
placeholder: `oneuptime-${props.eventType.toLowerCase()}-`,
|
||||
showIf: (
|
||||
formValue:
|
||||
| IncidentNotificationRule
|
||||
| AlertNotificationRule
|
||||
| ScheduledMaintenanceNotificationRule,
|
||||
) => {
|
||||
return formValue.shouldCreateNewChannel || false;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "shouldInviteOwnersToNewChannel",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import PageComponentProps from "../../PageComponentProps";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import ExecutionLogsTable from "../../../Components/OnCallPolicy/ExecutionLogs/ExecutionLogsTable";
|
||||
|
||||
const IncidentDelete: FunctionComponent<
|
||||
PageComponentProps
|
||||
> = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return <ExecutionLogsTable alertId={modelId} />;
|
||||
};
|
||||
|
||||
export default IncidentDelete;
|
||||
@@ -85,12 +85,27 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="On Call">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "On Call Executions",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS
|
||||
] as Route,
|
||||
{ modelId: props.modelId },
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Call}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Alert Notes">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Private Notes",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ALERT_INTERNAL_NOTE] as Route,
|
||||
RouteMap[PageMap.ALERT_VIEW_INTERNAL_NOTE] as Route,
|
||||
{ modelId: props.modelId },
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import PageComponentProps from "../../PageComponentProps";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import ExecutionLogsTable from "../../../Components/OnCallPolicy/ExecutionLogs/ExecutionLogsTable";
|
||||
|
||||
const IncidentDelete: FunctionComponent<
|
||||
PageComponentProps
|
||||
> = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return <ExecutionLogsTable incidentId={modelId} />;
|
||||
};
|
||||
|
||||
export default IncidentDelete;
|
||||
@@ -5,6 +5,7 @@ import Incident from "Common/Models/DatabaseModels/Incident";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
|
||||
const IncidentDelete: FunctionComponent<
|
||||
PageComponentProps
|
||||
@@ -20,6 +21,19 @@ const IncidentDelete: FunctionComponent<
|
||||
"Why did this incident happen? Here is the root cause of this incident.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Root Cause"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: "Root Cause",
|
||||
|
||||
fieldType: FormFieldSchemaType.Markdown,
|
||||
required: false,
|
||||
placeholder: "Root Cause",
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
showDetailsInNumberOfColumns: 1,
|
||||
modelType: Incident,
|
||||
|
||||
53
Dashboard/src/Pages/Incidents/View/Settings.tsx
Normal file
53
Dashboard/src/Pages/Incidents/View/Settings.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import PageComponentProps from "../../PageComponentProps";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import Incident from "Common/Models/DatabaseModels/Incident";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
|
||||
const IncidentDelete: FunctionComponent<
|
||||
PageComponentProps
|
||||
> = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<CardModelDetail
|
||||
name="Incident Settings"
|
||||
cardProps={{
|
||||
title: "Incident Settings",
|
||||
description: "Manage settings for this incident here.",
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit Settings"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
isVisibleOnStatusPage: true,
|
||||
},
|
||||
title: "Visible on Status Page",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
showDetailsInNumberOfColumns: 1,
|
||||
modelType: Incident,
|
||||
id: "model-detail-incident-settings",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
isVisibleOnStatusPage: true,
|
||||
},
|
||||
title: "Visible on Status Page",
|
||||
fieldType: FieldType.Boolean,
|
||||
},
|
||||
],
|
||||
modelId: modelId,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default IncidentDelete;
|
||||
@@ -85,12 +85,27 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="On Call">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "On Call Executions",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.INCIDENT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS
|
||||
] as Route,
|
||||
{ modelId: props.modelId },
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Call}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Incident Notes">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Private Notes",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_INTERNAL_NOTE] as Route,
|
||||
RouteMap[PageMap.INCIDENT_VIEW_INTERNAL_NOTE] as Route,
|
||||
{ modelId: props.modelId },
|
||||
),
|
||||
}}
|
||||
@@ -100,7 +115,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
link={{
|
||||
title: "Public Notes",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_PUBLIC_NOTE] as Route,
|
||||
RouteMap[PageMap.INCIDENT_VIEW_PUBLIC_NOTE] as Route,
|
||||
{ modelId: props.modelId },
|
||||
),
|
||||
}}
|
||||
@@ -120,6 +135,17 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
icon={IconProp.TableCells}
|
||||
/>
|
||||
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW_SETTINGS] as Route,
|
||||
{ modelId: props.modelId },
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Settings}
|
||||
/>
|
||||
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Delete Incident",
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import PageComponentProps from "../../PageComponentProps";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import ScheduledMaintenance from "Common/Models/DatabaseModels/ScheduledMaintenance";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
|
||||
const ScheduledMaintenanceDelete: FunctionComponent<
|
||||
PageComponentProps
|
||||
> = (): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<CardModelDetail
|
||||
name="Scheduled Maintenance Settings"
|
||||
cardProps={{
|
||||
title: "Scheduled Maintenance Settings",
|
||||
description: "Manage your scheduled maintenance event settings here.",
|
||||
}}
|
||||
editButtonText="Edit Settings"
|
||||
isEditable={true}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
isVisibleOnStatusPage: true,
|
||||
},
|
||||
title: "Visible on Status Page",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
showDetailsInNumberOfColumns: 1,
|
||||
modelType: ScheduledMaintenance,
|
||||
id: "model-detail-incident-settings",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
isVisibleOnStatusPage: true,
|
||||
},
|
||||
title: "Visible on Status Page",
|
||||
fieldType: FieldType.Boolean,
|
||||
},
|
||||
],
|
||||
modelId: modelId,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScheduledMaintenanceDelete;
|
||||
@@ -100,6 +100,17 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
icon={IconProp.TableCells}
|
||||
/>
|
||||
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS] as Route,
|
||||
{ modelId: props.modelId },
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Settings}
|
||||
/>
|
||||
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Delete Event",
|
||||
|
||||
@@ -32,7 +32,7 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
{/* <SideMenuSection title="Workspace Connections">
|
||||
<SideMenuSection title="Workspace Connections">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Slack",
|
||||
@@ -42,7 +42,7 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
|
||||
}}
|
||||
icon={IconProp.Slack}
|
||||
/>
|
||||
</SideMenuSection> */}
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Monitors">
|
||||
<SideMenuItem
|
||||
|
||||
@@ -23,6 +23,14 @@ const StatusPageDelete: FunctionComponent<
|
||||
editButtonText="Edit Settings"
|
||||
isEditable={true}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
showIncidentsOnStatusPage: true,
|
||||
},
|
||||
title: "Show Incidents",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
showIncidentHistoryInDays: true,
|
||||
@@ -46,6 +54,14 @@ const StatusPageDelete: FunctionComponent<
|
||||
modelType: StatusPage,
|
||||
id: "model-detail-status-page",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
showIncidentsOnStatusPage: true,
|
||||
},
|
||||
fieldType: FieldType.Boolean,
|
||||
title: "Show Incidents",
|
||||
placeholder: "No",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
showIncidentHistoryInDays: true,
|
||||
@@ -75,6 +91,14 @@ const StatusPageDelete: FunctionComponent<
|
||||
editButtonText="Edit Settings"
|
||||
isEditable={true}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
showAnnouncementsOnStatusPage: true,
|
||||
},
|
||||
title: "Show Announcements",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
showAnnouncementHistoryInDays: true,
|
||||
@@ -90,6 +114,14 @@ const StatusPageDelete: FunctionComponent<
|
||||
modelType: StatusPage,
|
||||
id: "model-detail-status-page",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
showAnnouncementsOnStatusPage: true,
|
||||
},
|
||||
fieldType: FieldType.Boolean,
|
||||
title: "Show Announcements",
|
||||
placeholder: "No",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
showAnnouncementHistoryInDays: true,
|
||||
@@ -111,6 +143,14 @@ const StatusPageDelete: FunctionComponent<
|
||||
editButtonText="Edit Settings"
|
||||
isEditable={true}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
showScheduledMaintenanceEventsOnStatusPage: true,
|
||||
},
|
||||
title: "Show Scheduled Maintenance Events",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
showScheduledEventHistoryInDays: true,
|
||||
@@ -134,6 +174,14 @@ const StatusPageDelete: FunctionComponent<
|
||||
modelType: StatusPage,
|
||||
id: "model-detail-status-page",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
showScheduledMaintenanceEventsOnStatusPage: true,
|
||||
},
|
||||
fieldType: FieldType.Boolean,
|
||||
title: "Show Scheduled Maintenance Events",
|
||||
placeholder: "No",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
showScheduledEventHistoryInDays: true,
|
||||
|
||||
@@ -78,7 +78,7 @@ const DashboardSideMenu: () => ReactElement = (): ReactElement => {
|
||||
subItemLink={subItemMenuLink}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
{/* <SideMenuSection title="Workspace Connections">
|
||||
<SideMenuSection title="Workspace Connections">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Slack",
|
||||
@@ -88,7 +88,7 @@ const DashboardSideMenu: () => ReactElement = (): ReactElement => {
|
||||
}}
|
||||
icon={IconProp.Slack}
|
||||
/>
|
||||
</SideMenuSection> */}
|
||||
</SideMenuSection>
|
||||
</SideMenu>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -35,6 +35,8 @@ import React, {
|
||||
} from "react";
|
||||
import { Edge, Node } from "reactflow";
|
||||
import { useAsyncEffect } from "use-async-effect";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
|
||||
const Delete: FunctionComponent<PageComponentProps> = (): ReactElement => {
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
@@ -310,14 +312,19 @@ const Delete: FunctionComponent<PageComponentProps> = (): ReactElement => {
|
||||
}}
|
||||
onRun={async (component: NodeDataProp) => {
|
||||
try {
|
||||
await API.post(
|
||||
URL.fromString(WORKFLOW_URL.toString()).addRoute(
|
||||
"/manual/run/" + modelId.toString(),
|
||||
),
|
||||
{
|
||||
data: component.returnValues,
|
||||
},
|
||||
);
|
||||
const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post(
|
||||
URL.fromString(WORKFLOW_URL.toString()).addRoute(
|
||||
"/manual/run/" + modelId.toString(),
|
||||
),
|
||||
{
|
||||
data: component.returnValues,
|
||||
},
|
||||
);
|
||||
|
||||
if (result instanceof HTTPErrorResponse) {
|
||||
throw result;
|
||||
}
|
||||
|
||||
setShowRunSuccessConfirmation(true);
|
||||
} catch (err) {
|
||||
|
||||
@@ -24,6 +24,13 @@ const AlertView: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
|
||||
return import("../Pages/Alerts/View/Index");
|
||||
},
|
||||
);
|
||||
|
||||
const AlertOnCallPolicyExecutionLogs: LazyExoticComponent<
|
||||
FunctionComponent<ComponentProps>
|
||||
> = lazy(() => {
|
||||
return import("../Pages/Alerts/View/OnCallPolicyExecutionLogs");
|
||||
});
|
||||
|
||||
const AlertViewDelete: LazyExoticComponent<FunctionComponent<ComponentProps>> =
|
||||
lazy(() => {
|
||||
return import("../Pages/Alerts/View/Delete");
|
||||
@@ -178,12 +185,12 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.ALERT_INTERNAL_NOTE)}
|
||||
path={RouteUtil.getLastPathForKey(PageMap.ALERT_VIEW_INTERNAL_NOTE)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<AlertInternalNote
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.ALERT_INTERNAL_NOTE] as Route}
|
||||
pageRoute={RouteMap[PageMap.ALERT_VIEW_INTERNAL_NOTE] as Route}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
@@ -212,6 +219,24 @@ const AlertsRoutes: FunctionComponent<ComponentProps> = (
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS,
|
||||
)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<AlertOnCallPolicyExecutionLogs
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</PageRoute>
|
||||
</Routes>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,19 @@ const Incidents: LazyExoticComponent<FunctionComponent<ComponentProps>> = lazy(
|
||||
return import("../Pages/Incidents/Incidents");
|
||||
},
|
||||
);
|
||||
|
||||
const IncidentViewSettings: LazyExoticComponent<
|
||||
FunctionComponent<ComponentProps>
|
||||
> = lazy(() => {
|
||||
return import("../Pages/Incidents/View/Settings");
|
||||
});
|
||||
|
||||
const IncidentViewOnCallPolicyExecutionLogs: LazyExoticComponent<
|
||||
FunctionComponent<ComponentProps>
|
||||
> = lazy(() => {
|
||||
return import("../Pages/Incidents/View/OnCallPolicyExecutionLogs");
|
||||
});
|
||||
|
||||
const IncidentView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
|
||||
lazy(() => {
|
||||
return import("../Pages/Incidents/View/Index");
|
||||
@@ -162,6 +175,18 @@ const IncidentsRoutes: FunctionComponent<ComponentProps> = (
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.INCIDENT_VIEW_SETTINGS)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<IncidentViewSettings
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.INCIDENT_VIEW_SETTINGS] as Route}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.INCIDENT_VIEW_STATE_TIMELINE,
|
||||
@@ -215,12 +240,16 @@ const IncidentsRoutes: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.INCIDENT_INTERNAL_NOTE)}
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.INCIDENT_VIEW_INTERNAL_NOTE,
|
||||
)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<IncidentInternalNote
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.INCIDENT_INTERNAL_NOTE] as Route}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.INCIDENT_VIEW_INTERNAL_NOTE] as Route
|
||||
}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
@@ -243,12 +272,12 @@ const IncidentsRoutes: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.INCIDENT_PUBLIC_NOTE)}
|
||||
path={RouteUtil.getLastPathForKey(PageMap.INCIDENT_VIEW_PUBLIC_NOTE)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<IncidentPublicNote
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.INCIDENT_PUBLIC_NOTE] as Route}
|
||||
pageRoute={RouteMap[PageMap.INCIDENT_VIEW_PUBLIC_NOTE] as Route}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
@@ -265,6 +294,24 @@ const IncidentsRoutes: FunctionComponent<ComponentProps> = (
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.INCIDENT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS,
|
||||
)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<IncidentViewOnCallPolicyExecutionLogs
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.INCIDENT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</PageRoute>
|
||||
</Routes>
|
||||
);
|
||||
|
||||
@@ -42,6 +42,13 @@ const ScheduledMaintenanceEventViewOwner: LazyExoticComponent<
|
||||
> = lazy(() => {
|
||||
return import("../Pages/ScheduledMaintenanceEvents/View/Owners");
|
||||
});
|
||||
|
||||
const ScheduledMaintenanceEventsViewSettings: LazyExoticComponent<
|
||||
FunctionComponent<ComponentProps>
|
||||
> = lazy(() => {
|
||||
return import("../Pages/ScheduledMaintenanceEvents/View/Settings");
|
||||
});
|
||||
|
||||
const ScheduledMaintenanceEventViewStateTimeline: LazyExoticComponent<
|
||||
FunctionComponent<ComponentProps>
|
||||
> = lazy(() => {
|
||||
@@ -191,6 +198,21 @@ const ScheduledMaintenanceEventsRoutes: FunctionComponent<ComponentProps> = (
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS,
|
||||
)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<ScheduledMaintenanceEventsViewSettings
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS] as Route
|
||||
}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.SCHEDULED_MAINTENANCE_VIEW_DELETE,
|
||||
|
||||
@@ -28,7 +28,11 @@ export function getAlertsBreadcrumbs(path: string): Array<Link> | undefined {
|
||||
"View Alert",
|
||||
"Owners",
|
||||
]),
|
||||
...BuildBreadcrumbLinksByTitles(PageMap.ALERT_INTERNAL_NOTE, [
|
||||
...BuildBreadcrumbLinksByTitles(
|
||||
PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS,
|
||||
["Project", "Alerts", "View Alert", "On Call Executions"],
|
||||
),
|
||||
...BuildBreadcrumbLinksByTitles(PageMap.ALERT_VIEW_INTERNAL_NOTE, [
|
||||
"Project",
|
||||
"Alerts",
|
||||
"View Alert",
|
||||
|
||||
@@ -56,13 +56,18 @@ export function getIncidentsBreadcrumbs(path: string): Array<Link> | undefined {
|
||||
"View Incident",
|
||||
"Owners",
|
||||
]),
|
||||
...BuildBreadcrumbLinksByTitles(PageMap.INCIDENT_INTERNAL_NOTE, [
|
||||
|
||||
...BuildBreadcrumbLinksByTitles(
|
||||
PageMap.INCIDENT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS,
|
||||
["Project", "Incidents", "View Incident", "On Call Executions"],
|
||||
),
|
||||
...BuildBreadcrumbLinksByTitles(PageMap.INCIDENT_VIEW_INTERNAL_NOTE, [
|
||||
"Project",
|
||||
"Incidents",
|
||||
"View Incident",
|
||||
"Private Notes",
|
||||
]),
|
||||
...BuildBreadcrumbLinksByTitles(PageMap.INCIDENT_PUBLIC_NOTE, [
|
||||
...BuildBreadcrumbLinksByTitles(PageMap.INCIDENT_VIEW_PUBLIC_NOTE, [
|
||||
"Project",
|
||||
"Incidents",
|
||||
"View Incident",
|
||||
@@ -80,6 +85,12 @@ export function getIncidentsBreadcrumbs(path: string): Array<Link> | undefined {
|
||||
"View Incident",
|
||||
"Delete Incident",
|
||||
]),
|
||||
...BuildBreadcrumbLinksByTitles(PageMap.INCIDENT_VIEW_SETTINGS, [
|
||||
"Project",
|
||||
"Incidents",
|
||||
"View Incident",
|
||||
"Settings",
|
||||
]),
|
||||
};
|
||||
return breadcrumpLinksMap[path];
|
||||
}
|
||||
|
||||
@@ -71,6 +71,15 @@ export function getScheduleMaintenanceBreadcrumbs(
|
||||
"Custom Fields",
|
||||
],
|
||||
),
|
||||
...BuildBreadcrumbLinksByTitles(
|
||||
PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS,
|
||||
[
|
||||
"Project",
|
||||
"Scheduled Maintenance Events",
|
||||
"View Scheduled Maintenance Event",
|
||||
"Settings",
|
||||
],
|
||||
),
|
||||
...BuildBreadcrumbLinksByTitles(PageMap.SCHEDULED_MAINTENANCE_VIEW_DELETE, [
|
||||
"Project",
|
||||
"Scheduled Maintenance Events",
|
||||
|
||||
@@ -74,14 +74,16 @@ enum PageMap {
|
||||
INCIDENT_CREATE = "INCIDENT_CREATE",
|
||||
INCIDENT_VIEW = "INCIDENT_VIEW",
|
||||
INCIDENT_VIEW_DELETE = "INCIDENT_VIEW_DELETE",
|
||||
INCIDENT_VIEW_SETTINGS = "INCIDENT_VIEW_SETTINGS",
|
||||
INCIDENT_VIEW_STATE_TIMELINE = "INCIDENT_VIEW_STATE_TIMELINE",
|
||||
INCIDENT_VIEW_ROOT_CAUSE = "INCIDENT_VIEW_ROOT_CAUSE",
|
||||
INCIDENT_VIEW_REMEDIATION = "INCIDENT_VIEW_REMEDIATION",
|
||||
INCIDENT_VIEW_DESCRIPTION = "INCIDENT_VIEW_DESCRIPTION",
|
||||
INCIDENT_INTERNAL_NOTE = "INCIDENT_INTERNAL_NOTE",
|
||||
INCIDENT_PUBLIC_NOTE = "INCIDENT_PUBLIC_NOTE",
|
||||
INCIDENT_VIEW_INTERNAL_NOTE = "INCIDENT_VIEW_INTERNAL_NOTE",
|
||||
INCIDENT_VIEW_PUBLIC_NOTE = "INCIDENT_VIEW_PUBLIC_NOTE",
|
||||
INCIDENT_VIEW_CUSTOM_FIELDS = "INCIDENT_VIEW_CUSTOM_FIELDS",
|
||||
INCIDENT_VIEW_OWNERS = "INCIDENT_VIEW_OWNERS",
|
||||
INCIDENT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS = "INCIDENT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS",
|
||||
|
||||
ALERTS_ROOT = "ALERTS_ROOT",
|
||||
ALERTS = "ALERTS",
|
||||
@@ -89,12 +91,13 @@ enum PageMap {
|
||||
ALERT_VIEW = "ALERT_VIEW",
|
||||
ALERT_VIEW_DELETE = "ALERT_VIEW_DELETE",
|
||||
ALERT_VIEW_STATE_TIMELINE = "ALERT_VIEW_STATE_TIMELINE",
|
||||
ALERT_INTERNAL_NOTE = "ALERT_INTERNAL_NOTE",
|
||||
ALERT_VIEW_INTERNAL_NOTE = "ALERT_VIEW_INTERNAL_NOTE",
|
||||
ALERT_VIEW_CUSTOM_FIELDS = "ALERT_VIEW_CUSTOM_FIELDS",
|
||||
ALERT_VIEW_OWNERS = "ALERT_VIEW_OWNERS",
|
||||
ALERT_VIEW_DESCRIPTION = "ALERT_VIEW_DESCRIPTION",
|
||||
ALERT_VIEW_ROOT_CAUSE = "ALERT_VIEW_ROOT_CAUSE",
|
||||
ALERT_VIEW_REMEDIATION = "ALERT_VIEW_REMEDIATION",
|
||||
ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS = "ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS",
|
||||
|
||||
SCHEDULED_MAINTENANCE_EVENTS_ROOT = "SCHEDULED_MAINTENANCE_EVENTS_ROOT",
|
||||
SCHEDULED_MAINTENANCE_EVENTS = "SCHEDULED_MAINTENANCE_EVENTS",
|
||||
@@ -108,6 +111,7 @@ enum PageMap {
|
||||
SCHEDULED_MAINTENANCE_PUBLIC_NOTE = "SCHEDULED_MAINTENANCE_PUBLIC_NOTE",
|
||||
SCHEDULED_MAINTENANCE_VIEW_CUSTOM_FIELDS = "SCHEDULED_MAINTENANCE_VIEW_CUSTOM_FIELDS",
|
||||
SCHEDULED_MAINTENANCE_VIEW_OWNERS = "SCHEDULED_MAINTENANCE_VIEW_OWNERS",
|
||||
SCHEDULED_MAINTENANCE_VIEW_SETTINGS = "SCHEDULED_MAINTENANCE_VIEW_SETTINGS",
|
||||
|
||||
MONITORS = "MONITORS",
|
||||
MONITORS_ROOT = "MONITORS_ROOT",
|
||||
|
||||
@@ -147,10 +147,12 @@ export const IncidentsRoutePath: Dictionary<string> = {
|
||||
[PageMap.INCIDENT_VIEW_ROOT_CAUSE]: `${RouteParams.ModelID}/root-cause`,
|
||||
[PageMap.INCIDENT_VIEW_DESCRIPTION]: `${RouteParams.ModelID}/description`,
|
||||
[PageMap.INCIDENT_VIEW_OWNERS]: `${RouteParams.ModelID}/owners`,
|
||||
[PageMap.INCIDENT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS]: `${RouteParams.ModelID}/on-call-policy-execution-logs`,
|
||||
[PageMap.INCIDENT_VIEW_DELETE]: `${RouteParams.ModelID}/delete`,
|
||||
[PageMap.INCIDENT_VIEW_SETTINGS]: `${RouteParams.ModelID}/settings`,
|
||||
[PageMap.INCIDENT_VIEW_CUSTOM_FIELDS]: `${RouteParams.ModelID}/custom-fields`,
|
||||
[PageMap.INCIDENT_INTERNAL_NOTE]: `${RouteParams.ModelID}/internal-notes`,
|
||||
[PageMap.INCIDENT_PUBLIC_NOTE]: `${RouteParams.ModelID}/public-notes`,
|
||||
[PageMap.INCIDENT_VIEW_INTERNAL_NOTE]: `${RouteParams.ModelID}/internal-notes`,
|
||||
[PageMap.INCIDENT_VIEW_PUBLIC_NOTE]: `${RouteParams.ModelID}/public-notes`,
|
||||
};
|
||||
|
||||
export const AlertsRoutePath: Dictionary<string> = {
|
||||
@@ -158,12 +160,13 @@ export const AlertsRoutePath: Dictionary<string> = {
|
||||
[PageMap.ALERT_VIEW]: `${RouteParams.ModelID}`,
|
||||
[PageMap.ALERT_VIEW_STATE_TIMELINE]: `${RouteParams.ModelID}/state-timeline`,
|
||||
[PageMap.ALERT_VIEW_OWNERS]: `${RouteParams.ModelID}/owners`,
|
||||
[PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS]: `${RouteParams.ModelID}/on-call-policy-execution-logs`,
|
||||
[PageMap.ALERT_VIEW_DELETE]: `${RouteParams.ModelID}/delete`,
|
||||
[PageMap.ALERT_VIEW_DESCRIPTION]: `${RouteParams.ModelID}/description`,
|
||||
[PageMap.ALERT_VIEW_ROOT_CAUSE]: `${RouteParams.ModelID}/root-cause`,
|
||||
[PageMap.ALERT_VIEW_REMEDIATION]: `${RouteParams.ModelID}/remediation`,
|
||||
[PageMap.ALERT_VIEW_CUSTOM_FIELDS]: `${RouteParams.ModelID}/custom-fields`,
|
||||
[PageMap.ALERT_INTERNAL_NOTE]: `${RouteParams.ModelID}/internal-notes`,
|
||||
[PageMap.ALERT_VIEW_INTERNAL_NOTE]: `${RouteParams.ModelID}/internal-notes`,
|
||||
};
|
||||
|
||||
export const ScheduledMaintenanceEventsRoutePath: Dictionary<string> = {
|
||||
@@ -177,6 +180,7 @@ export const ScheduledMaintenanceEventsRoutePath: Dictionary<string> = {
|
||||
[PageMap.SCHEDULED_MAINTENANCE_INTERNAL_NOTE]: `${RouteParams.ModelID}/internal-notes`,
|
||||
[PageMap.SCHEDULED_MAINTENANCE_PUBLIC_NOTE]: `${RouteParams.ModelID}/public-notes`,
|
||||
[PageMap.SCHEDULED_MAINTENANCE_VIEW_CUSTOM_FIELDS]: `${RouteParams.ModelID}/custom-fields`,
|
||||
[PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS]: `${RouteParams.ModelID}/settings`,
|
||||
};
|
||||
|
||||
export const SettingsRoutePath: Dictionary<string> = {
|
||||
@@ -438,6 +442,12 @@ const RouteMap: Dictionary<Route> = {
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/alerts/${
|
||||
AlertsRoutePath[PageMap.ALERT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS]
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.ALERT_VIEW_DELETE]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/alerts/${
|
||||
AlertsRoutePath[PageMap.ALERT_VIEW_DELETE]
|
||||
@@ -468,9 +478,9 @@ const RouteMap: Dictionary<Route> = {
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.ALERT_INTERNAL_NOTE]: new Route(
|
||||
[PageMap.ALERT_VIEW_INTERNAL_NOTE]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/alerts/${
|
||||
AlertsRoutePath[PageMap.ALERT_INTERNAL_NOTE]
|
||||
AlertsRoutePath[PageMap.ALERT_VIEW_INTERNAL_NOTE]
|
||||
}`,
|
||||
),
|
||||
|
||||
@@ -549,27 +559,39 @@ const RouteMap: Dictionary<Route> = {
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.INCIDENT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/incidents/${
|
||||
IncidentsRoutePath[PageMap.INCIDENT_VIEW_ON_CALL_POLICY_EXECUTION_LOGS]
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.INCIDENT_VIEW_DELETE]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/incidents/${
|
||||
IncidentsRoutePath[PageMap.INCIDENT_VIEW_DELETE]
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.INCIDENT_VIEW_SETTINGS]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/incidents/${
|
||||
IncidentsRoutePath[PageMap.INCIDENT_VIEW_SETTINGS]
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.INCIDENT_VIEW_CUSTOM_FIELDS]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/incidents/${
|
||||
IncidentsRoutePath[PageMap.INCIDENT_VIEW_CUSTOM_FIELDS]
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.INCIDENT_INTERNAL_NOTE]: new Route(
|
||||
[PageMap.INCIDENT_VIEW_INTERNAL_NOTE]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/incidents/${
|
||||
IncidentsRoutePath[PageMap.INCIDENT_INTERNAL_NOTE]
|
||||
IncidentsRoutePath[PageMap.INCIDENT_VIEW_INTERNAL_NOTE]
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.INCIDENT_PUBLIC_NOTE]: new Route(
|
||||
[PageMap.INCIDENT_VIEW_PUBLIC_NOTE]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/incidents/${
|
||||
IncidentsRoutePath[PageMap.INCIDENT_PUBLIC_NOTE]
|
||||
IncidentsRoutePath[PageMap.INCIDENT_VIEW_PUBLIC_NOTE]
|
||||
}`,
|
||||
),
|
||||
|
||||
@@ -651,6 +673,14 @@ const RouteMap: Dictionary<Route> = {
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/scheduled-maintenance-events/${
|
||||
ScheduledMaintenanceEventsRoutePath[
|
||||
PageMap.SCHEDULED_MAINTENANCE_VIEW_SETTINGS
|
||||
]
|
||||
}`,
|
||||
),
|
||||
|
||||
[PageMap.SCHEDULED_MAINTENANCE_PUBLIC_NOTE]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/scheduled-maintenance-events/${
|
||||
ScheduledMaintenanceEventsRoutePath[
|
||||
|
||||
@@ -158,6 +158,104 @@ This is the response from the API:
|
||||
}
|
||||
```
|
||||
|
||||
#### Uptime API
|
||||
|
||||
This API will fetch all the uptime of all the resources on status page.
|
||||
|
||||
To get overall uptime of all the resources, you can make a POST request to the following endpoint:
|
||||
|
||||
```bash
|
||||
curl -X POST https://oneuptime.com/status-page-api/uptime/:statusPageId
|
||||
```
|
||||
|
||||
**Request Body (optional):**
|
||||
|
||||
You can send startDate and endDate as request body.
|
||||
|
||||
```
|
||||
{
|
||||
"startDate": "2021-09-01T00:00:00Z",
|
||||
"endDate": "2021-09-30T23:59:59Z"
|
||||
}
|
||||
```
|
||||
|
||||
These dates should not be more than 90 days apart. If you do not provide the dates, the API will return the uptime for the last 14 days.
|
||||
|
||||
**Example Response:**
|
||||
|
||||
This is the example response from the API:
|
||||
|
||||
```json
|
||||
{
|
||||
"statusPageResourceUptimes": [
|
||||
{
|
||||
"statusPageResourceId": {
|
||||
"_type": "ObjectID",
|
||||
"value": "cfffa3c3-fdf3-4cd7-9585-d6d408a14663"
|
||||
},
|
||||
"uptimePercent": 99.98,
|
||||
"statusPageResourceName": "Status Page Resource Name",
|
||||
"currentStatus": {
|
||||
"_id": "cc80b385-4190-42a3-ae8b-9b391e90d79f",
|
||||
"isPermissionIf": {},
|
||||
"name": "Operational",
|
||||
"color": {
|
||||
"_type": "Color",
|
||||
"value": "#2ab57d"
|
||||
},
|
||||
"isOperationalState": true,
|
||||
"priority": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"groupUptimes": [
|
||||
{
|
||||
"statusPageGroupId": {
|
||||
"_type": "ObjectID",
|
||||
"value": "df7632c4-c5c0-453c-88bf-9ee3d68d45f2"
|
||||
},
|
||||
"uptimePercent": 99.98,
|
||||
"statusPageResourceUptimes": [
|
||||
{
|
||||
"statusPageResourceId": {
|
||||
"_type": "ObjectID",
|
||||
"value": "8175534f-aa77-456c-ad5b-b8e7b85876aa"
|
||||
},
|
||||
"uptimePercent": 99.98,
|
||||
"statusPageResourceName": "dfg",
|
||||
"currentStatus": {
|
||||
"_id": "cc80b385-4190-42a3-ae8b-9b391e90d79f",
|
||||
"isPermissionIf": {},
|
||||
"name": "Operational",
|
||||
"color": {
|
||||
"_type": "Color",
|
||||
"value": "#2ab57d"
|
||||
},
|
||||
"isOperationalState": true,
|
||||
"priority": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"statusPageGroupName": "Group Name",
|
||||
"currentStatus": {
|
||||
"_id": "cc80b385-4190-42a3-ae8b-9b391e90d79f",
|
||||
"isPermissionIf": {},
|
||||
"name": "Operational",
|
||||
"color": {
|
||||
"_type": "Color",
|
||||
"value": "#2ab57d"
|
||||
},
|
||||
"isOperationalState": true,
|
||||
"priority": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"startDate": "2021-09-01T00:00:00Z",
|
||||
"endDate": "2021-09-30T23:59:59Z"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Incident API
|
||||
|
||||
This API will fetch all the incidents that are on the status page. To get all the incidents on the status page, you can make a POST request to the following endpoint:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.2-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:22.9
|
||||
FROM public.ecr.aws/docker/library/node:23.8
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM public.ecr.aws/docker/library/node:21.7.3-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ This repository is used to host all the helm-charts for OneUptime.
|
||||
|
||||
### Helm Packages
|
||||
|
||||
- *oneuptime* : Chart for deploying OneUpitme app. [Read Docs here](oneuptime/README.md)
|
||||
- *oneuptime* : Chart for deploying OneUptime app. [Read Docs here](oneuptime/README.md)
|
||||
|
||||
|
||||
|
||||
@@ -16,5 +16,5 @@ You should then be able to access OneUptime cluster with that IP. Please make su
|
||||
We would like to hear your feedback to make this product better for you and for other users, please email us at hello@oneuptime.com.
|
||||
- If you notice a bug, we will fix it for you.
|
||||
- If you need a feature, we will add that to the roadmap and let you know the estimated time to ship.
|
||||
- If you are an enterprise customer, we offer dedicated engineering support to build oneupitme features you need to integrate OneUptime for your organization. Please contact us at sales@oneuptime.com
|
||||
- If you are an enterprise customer, we offer dedicated engineering support to build oneuptime features you need to integrate OneUptime for your organization. Please contact us at sales@oneuptime.com
|
||||
We would love to hear your feedback. Email: hello@oneuptime.com
|
||||
@@ -56,6 +56,8 @@ Usage:
|
||||
value: {{ $.Values.analytics.host }}
|
||||
- name: SERVER_ACCOUNTS_HOSTNAME
|
||||
value: {{ $.Release.Name }}-accounts.{{ $.Release.Namespace }}.svc.{{ $.Values.global.clusterDomain }}
|
||||
- name: SERVER_SERVER_MONITOR_INGEST_HOSTNAME
|
||||
value: {{ $.Release.Name }}-server-monitor-ingest.{{ $.Release.Namespace }}.svc.{{ $.Values.global.clusterDomain }}
|
||||
- name: SERVER_ISOLATED_VM_HOSTNAME
|
||||
value: {{ $.Release.Name }}-isolated-vm.{{ $.Release.Namespace }}.svc.{{ $.Values.global.clusterDomain }}
|
||||
- name: SERVER_WORKFLOW_HOSTNAME
|
||||
@@ -95,6 +97,8 @@ Usage:
|
||||
value: {{ $.Values.port.app | squote }}
|
||||
- name: PROBE_INGEST_PORT
|
||||
value: {{ $.Values.port.probeIngest | squote }}
|
||||
- name: SERVER_MONITOR_INGEST_PORT
|
||||
value: {{ $.Values.port.serverMonitorIngest | squote }}
|
||||
- name: OPEN_TELEMETRY_INGEST_PORT
|
||||
value: {{ $.Values.port.openTelemetryIngest | squote }}
|
||||
- name: INCOMING_REQUEST_INGEST_PORT
|
||||
|
||||
113
HelmChart/Public/oneuptime/templates/server-monitor-ingest.yaml
Normal file
113
HelmChart/Public/oneuptime/templates/server-monitor-ingest.yaml
Normal file
@@ -0,0 +1,113 @@
|
||||
# OneUptime server-monitor-ingest Deployment
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ printf "%s-%s" $.Release.Name "server-monitor-ingest" }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "server-monitor-ingest" }}
|
||||
app.kubernetes.io/part-of: oneuptime
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
appname: oneuptime
|
||||
date: "{{ now | unixEpoch }}"
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "server-monitor-ingest" }}
|
||||
{{- if $.Values.deployment.serverMonitorIngest.replicaCount }}
|
||||
replicas: {{ $.Values.deployment.serverMonitorIngest.replicaCount }}
|
||||
{{- else }}
|
||||
replicas: {{ $.Values.deployment.replicaCount }}
|
||||
{{- end }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "server-monitor-ingest" }}
|
||||
date: "{{ now | unixEpoch }}"
|
||||
appname: oneuptime
|
||||
spec:
|
||||
volumes:
|
||||
- name: greenlockrc
|
||||
emptyDir:
|
||||
sizeLimit: "1Gi"
|
||||
{{- if $.Values.podSecurityContext }}
|
||||
securityContext: {{- $.Values.podSecurityContext | toYaml | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $.Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml $.Values.imagePullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $.Values.affinity }}
|
||||
affinity: {{- $.Values.affinity | toYaml | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $.Values.tolerations }}
|
||||
tolerations: {{- $.Values.tolerations | toYaml | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $.Values.nodeSelector }}
|
||||
nodeSelector: {{- $.Values.nodeSelector | toYaml | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- image: {{ printf "%s/%s/%s:%s" $.Values.image.registry $.Values.image.repository "server-monitor-ingest" $.Values.image.tag }}
|
||||
name: {{ printf "%s-%s" $.Release.Name "server-monitor-ingest" }}
|
||||
{{- if $.Values.startupProbe.enabled }}
|
||||
# Startup probe
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /status/live
|
||||
port: {{ $.Values.port.serverMonitorIngest }}
|
||||
periodSeconds: {{ $.Values.startupProbe.periodSeconds }}
|
||||
failureThreshold: {{ $.Values.startupProbe.failureThreshold }}
|
||||
{{- end }}
|
||||
{{- if $.Values.livenessProbe.enabled }}
|
||||
# Liveness probe
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /status/live
|
||||
port: {{ $.Values.port.serverMonitorIngest }}
|
||||
periodSeconds: {{ $.Values.livenessProbe.periodSeconds }}
|
||||
timeoutSeconds: {{ $.Values.livenessProbe.timeoutSeconds }}
|
||||
initialDelaySeconds: {{ $.Values.livenessProbe.initialDelaySeconds }}
|
||||
{{- end }}
|
||||
{{- if $.Values.readinessProbe.enabled }}
|
||||
# Readyness Probe
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /status/ready
|
||||
port: {{ $.Values.port.serverMonitorIngest }}
|
||||
periodSeconds: {{ $.Values.readinessProbe.periodSeconds }}
|
||||
initialDelaySeconds: {{ $.Values.readinessProbe.initialDelaySeconds }}
|
||||
timeoutSeconds: {{ $.Values.readinessProbe.timeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- if $.Values.containerSecurityContext }}
|
||||
securityContext: {{- $.Values.containerSecurityContext | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
imagePullPolicy: {{ $.Values.image.pullPolicy }}
|
||||
env:
|
||||
{{- include "oneuptime.env.common" . | nindent 12 }}
|
||||
{{- include "oneuptime.env.commonServer" . | nindent 12 }}
|
||||
{{- include "oneuptime.env.oneuptimeSecret" . | nindent 12 }}
|
||||
- name: OPENTELEMETRY_EXPORTER_OTLP_HEADERS
|
||||
value: {{ $.Values.openTelemetryExporter.headers }}
|
||||
- name: PORT
|
||||
value: {{ $.Values.port.serverMonitorIngest | quote }}
|
||||
- name: DISABLE_TELEMETRY
|
||||
value: {{ $.Values.serverMonitorIngest.disableTelemetryCollection | quote }}
|
||||
ports:
|
||||
- containerPort: {{ $.Values.port.serverMonitorIngest }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
restartPolicy: {{ $.Values.image.restartPolicy }}
|
||||
|
||||
---
|
||||
|
||||
# OneUptime server-monitor-ingest Service
|
||||
{{- $serverMonitorIngestPorts := dict "port" $.Values.port.serverMonitorIngest -}}
|
||||
{{- $serverMonitorIngestServiceArgs := dict "ServiceName" "server-monitor-ingest" "Ports" $serverMonitorIngestPorts "Release" $.Release "Values" $.Values -}}
|
||||
{{- include "oneuptime.service" $serverMonitorIngestServiceArgs }}
|
||||
---
|
||||
|
||||
# OneUptime server-monitor-ingest autoscaler
|
||||
{{- $serverMonitorIngestAutoScalerArgs := dict "ServiceName" "server-monitor-ingest" "Release" $.Release "Values" $.Values -}}
|
||||
{{- include "oneuptime.autoscaler" $serverMonitorIngestAutoScalerArgs }}
|
||||
---
|
||||
@@ -31,6 +31,8 @@ deployment:
|
||||
replicaCount: 1
|
||||
probeIngest:
|
||||
replicaCount:
|
||||
serverMonitorIngest:
|
||||
replicaCount:
|
||||
openTelemetryIngest:
|
||||
replicaCount:
|
||||
fluentIngest:
|
||||
@@ -221,6 +223,7 @@ probes:
|
||||
port:
|
||||
app: 3002
|
||||
probeIngest: 3400
|
||||
serverMonitorIngest: 3404
|
||||
openTelemetryIngest: 3403
|
||||
fluentIngest: 3401
|
||||
incomingRequestIngest: 3402
|
||||
@@ -472,6 +475,9 @@ incomingRequestIngest:
|
||||
isolatedVM:
|
||||
disableTelemetryCollection: false
|
||||
|
||||
serverMonitorIngest:
|
||||
disableTelemetryCollection: false
|
||||
|
||||
|
||||
slackApp:
|
||||
clientId:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.7.3-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:22.9
|
||||
FROM public.ecr.aws/docker/library/node:23.8
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -14,6 +14,11 @@ upstream open-telemetry-ingest {
|
||||
server ${SERVER_OPEN_TELEMETRY_INGEST_HOSTNAME}:${OPEN_TELEMETRY_INGEST_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
|
||||
|
||||
upstream server-monitor-ingest {
|
||||
server ${SERVER_SERVER_MONITOR_INGEST_HOSTNAME}:${SERVER_MONITOR_INGEST_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
|
||||
upstream incoming-request-ingest {
|
||||
server ${SERVER_INCOMING_REQUEST_INGEST_HOSTNAME}:${INCOMING_REQUEST_INGEST_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
@@ -630,7 +635,7 @@ server {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_pass http://probe-ingest/server-monitor;
|
||||
proxy_pass http://server-monitor-ingest/server-monitor;
|
||||
|
||||
client_max_body_size 50M;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:22.9
|
||||
FROM public.ecr.aws/docker/library/node:23.8
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:22.9
|
||||
FROM public.ecr.aws/docker/library/node:23.8
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:22.9
|
||||
FROM public.ecr.aws/docker/library/node:23.8
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import MonitorAPI from "./API/Monitor";
|
||||
import ProbeIngest from "./API/Probe";
|
||||
import RegisterAPI from "./API/Register";
|
||||
import ServerMonitorAPI from "./API/ServerMonitor";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import { ClickhouseAppInstance } from "Common/Server/Infrastructure/ClickhouseDatabase";
|
||||
import PostgresAppInstance from "Common/Server/Infrastructure/PostgresDatabase";
|
||||
@@ -22,7 +21,6 @@ const APP_NAME: string = "probe-ingest";
|
||||
app.use([`/${APP_NAME}`, "/ingestor", "/"], RegisterAPI);
|
||||
app.use([`/${APP_NAME}`, "/ingestor", "/"], MonitorAPI);
|
||||
app.use([`/${APP_NAME}`, "/ingestor", "/"], ProbeIngest);
|
||||
app.use([`/${APP_NAME}`, "/ingestor", "/"], ServerMonitorAPI);
|
||||
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
|
||||
56
ServerMonitorIngest/.dockerignore
Normal file
56
ServerMonitorIngest/.dockerignore
Normal file
@@ -0,0 +1,56 @@
|
||||
.git
|
||||
|
||||
node_modules
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
node_modules
|
||||
|
||||
.idea
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
env.js
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
yarn.lock
|
||||
Untitled-1
|
||||
*.local.sh
|
||||
*.local.yaml
|
||||
run
|
||||
stop
|
||||
|
||||
nohup.out*
|
||||
|
||||
encrypted-credentials.tar
|
||||
encrypted-credentials/
|
||||
|
||||
_README.md
|
||||
|
||||
# Important Add production values to gitignore.
|
||||
values-saas-production.yaml
|
||||
kubernetes/values-saas-production.yaml
|
||||
|
||||
/private
|
||||
|
||||
/tls_cert.pem
|
||||
/tls_key.pem
|
||||
/keys
|
||||
|
||||
temp_readme.md
|
||||
|
||||
tests/coverage
|
||||
|
||||
settings.json
|
||||
|
||||
GoSDK/tester/
|
||||
1
ServerMonitorIngest/.gitattributes
vendored
Normal file
1
ServerMonitorIngest/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.js text eol=lf
|
||||
16
ServerMonitorIngest/.gitignore
vendored
Normal file
16
ServerMonitorIngest/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
#/backend/node_modules
|
||||
/kubernetes
|
||||
/node_modules
|
||||
.idea
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
yarn.lock
|
||||
83
ServerMonitorIngest/Dockerfile.tpl
Normal file
83
ServerMonitorIngest/Dockerfile.tpl
Normal file
@@ -0,0 +1,83 @@
|
||||
#
|
||||
# OneUptime-ServerMonitorIngest Dockerfile
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:23.8
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
RUN npm config set fetch-retry-mintimeout 100000
|
||||
RUN npm config set fetch-retry-maxtimeout 600000
|
||||
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
|
||||
ENV GIT_SHA=${GIT_SHA}
|
||||
ENV APP_VERSION=${APP_VERSION}
|
||||
ENV NODE_OPTIONS="--use-openssl-ca"
|
||||
|
||||
## Add Intermediate Certs
|
||||
COPY ./SslCertificates /usr/local/share/ca-certificates
|
||||
RUN update-ca-certificates
|
||||
|
||||
|
||||
# IF APP_VERSION is not set, set it to 1.0.0
|
||||
RUN if [ -z "$APP_VERSION" ]; then export APP_VERSION=1.0.0; fi
|
||||
|
||||
|
||||
RUN apt-get update
|
||||
|
||||
# Install bash.
|
||||
RUN apt-get install bash -y && apt-get install curl -y && apt-get install iputils-ping -y
|
||||
|
||||
# Install python
|
||||
RUN apt-get update && apt-get install -y .gyp python3 make g++
|
||||
|
||||
# Install playwright dependencies
|
||||
RUN apt-get install -y libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libgtk-3-0 libpango-1.0-0 libcairo2 libgdk-pixbuf2.0-0 libasound2 libatspi2.0-0
|
||||
|
||||
#Use bash shell by default
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
# Install iputils
|
||||
RUN apt-get install net-tools -y
|
||||
|
||||
RUN mkdir -p /usr/src
|
||||
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
# Set version in ./Common/package.json to the APP_VERSION
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/Common/package.json
|
||||
RUN npm install
|
||||
COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
|
||||
ENV PRODUCTION=true
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
RUN npx playwright install --with-deps
|
||||
|
||||
# Install app dependencies
|
||||
COPY ./ServerMonitorIngest/package*.json /usr/src/app/
|
||||
RUN npm install
|
||||
|
||||
# Expose ports.
|
||||
# - 3404: OneUptime-server-monitor-ingest
|
||||
EXPOSE 3404
|
||||
|
||||
{{ if eq .Env.ENVIRONMENT "development" }}
|
||||
#Run the app
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
{{ else }}
|
||||
# Copy app source
|
||||
COPY ./ServerMonitorIngest /usr/src/app
|
||||
# Bundle app source
|
||||
RUN npm run compile
|
||||
#Run the app
|
||||
CMD [ "npm", "start" ]
|
||||
{{ end }}
|
||||
|
||||
69
ServerMonitorIngest/Index.ts
Normal file
69
ServerMonitorIngest/Index.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import ServerMonitorAPI from "./API/ServerMonitor";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import { ClickhouseAppInstance } from "Common/Server/Infrastructure/ClickhouseDatabase";
|
||||
import PostgresAppInstance from "Common/Server/Infrastructure/PostgresDatabase";
|
||||
import Redis from "Common/Server/Infrastructure/Redis";
|
||||
import InfrastructureStatus from "Common/Server/Infrastructure/Status";
|
||||
import Express, { ExpressApplication } from "Common/Server/Utils/Express";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import Realtime from "Common/Server/Utils/Realtime";
|
||||
import App from "Common/Server/Utils/StartServer";
|
||||
import Telemetry from "Common/Server/Utils/Telemetry";
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
const APP_NAME: string = "server-monitor-ingest";
|
||||
|
||||
app.use([`/${APP_NAME}`, "/"], ServerMonitorAPI);
|
||||
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
const statusCheck: PromiseVoidFunction = async (): Promise<void> => {
|
||||
return await InfrastructureStatus.checkStatusWithRetry({
|
||||
checkClickhouseStatus: true,
|
||||
checkPostgresStatus: true,
|
||||
checkRedisStatus: true,
|
||||
retryCount: 3,
|
||||
});
|
||||
};
|
||||
|
||||
// Initialize telemetry
|
||||
Telemetry.init({
|
||||
serviceName: APP_NAME,
|
||||
});
|
||||
|
||||
// init the app
|
||||
await App.init({
|
||||
appName: APP_NAME,
|
||||
statusOptions: {
|
||||
liveCheck: statusCheck,
|
||||
readyCheck: statusCheck,
|
||||
},
|
||||
});
|
||||
|
||||
// connect to the database.
|
||||
await PostgresAppInstance.connect();
|
||||
|
||||
// connect redis
|
||||
await Redis.connect();
|
||||
|
||||
await ClickhouseAppInstance.connect(
|
||||
ClickhouseAppInstance.getDatasourceOptions(),
|
||||
);
|
||||
|
||||
await Realtime.init();
|
||||
|
||||
// add default routes
|
||||
await App.addDefaultRoutes();
|
||||
} catch (err) {
|
||||
logger.error("App Init Failed:");
|
||||
logger.error(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
init().catch((err: Error) => {
|
||||
logger.error(err);
|
||||
logger.error("Exiting node process");
|
||||
process.exit(1);
|
||||
});
|
||||
32
ServerMonitorIngest/jest.config.json
Normal file
32
ServerMonitorIngest/jest.config.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
|
||||
"preset": "ts-jest",
|
||||
"testPathIgnorePatterns": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
],
|
||||
"verbose": true,
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"tsconfig": "tsconfig.json",
|
||||
"babelConfig": false
|
||||
}
|
||||
},
|
||||
"moduleFileExtensions": ["ts", "js", "json"],
|
||||
"transform": {
|
||||
".(ts|tsx)": "ts-jest"
|
||||
},
|
||||
"testEnvironment": "node",
|
||||
"collectCoverage": false,
|
||||
"coverageReporters": ["text", "lcov"],
|
||||
"testRegex": "./Tests/(.*).test.ts",
|
||||
"collectCoverageFrom": ["./**/*.(tsx||ts)"],
|
||||
"coverageThreshold": {
|
||||
"global": {
|
||||
"lines": 0,
|
||||
"functions": 0,
|
||||
"branches": 0,
|
||||
"statements": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
5
ServerMonitorIngest/nodemon.json
Normal file
5
ServerMonitorIngest/nodemon.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"watch": ["./","../Common/Server", "../Common/Types", "../Common/Utils", "../Common/Models"],
|
||||
"ext": "ts,json,tsx,env,js,jsx,hbs",
|
||||
"exec": "node --inspect=0.0.0.0:9229 --require ts-node/register Index.ts"
|
||||
}
|
||||
4698
ServerMonitorIngest/package-lock.json
generated
Normal file
4698
ServerMonitorIngest/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
ServerMonitorIngest/package.json
Normal file
28
ServerMonitorIngest/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@oneuptime/server-monitor-ingest",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node --require ts-node/register Index.ts",
|
||||
"compile": "tsc",
|
||||
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
|
||||
"dev": "npx nodemon",
|
||||
"audit": "npm audit --audit-level=low",
|
||||
"dep-check": "npm install -g depcheck && depcheck ./ --skip-missing=true",
|
||||
"test": "jest --passWithNoTests"
|
||||
},
|
||||
"author": "OneUptime <hello@oneuptime.com> (https://oneuptime.com/)",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"ts-node": "^10.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.5.0",
|
||||
"@types/node": "^17.0.31",
|
||||
"jest": "^28.1.0",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-jest": "^28.0.2"
|
||||
}
|
||||
}
|
||||
115
ServerMonitorIngest/tsconfig.json
Normal file
115
ServerMonitorIngest/tsconfig.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"ts-node": {
|
||||
// these options are overrides used only by ts-node
|
||||
// same as the --compilerOptions flag and the TS_NODE_COMPILER_OPTIONS environment variable
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"resolveJsonModule": true,
|
||||
}
|
||||
},
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2017" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"jsx": "react" /* Specify what JSX code is generated. */,
|
||||
"experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */,
|
||||
"emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */,
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
|
||||
/* Modules */
|
||||
// "module": "es2022" /* Specify what module code is generated. */,
|
||||
"rootDir": "" /* Specify the root folder within your source files. */,
|
||||
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
] /* Specify multiple folders that act like `./node_modules/@types`. */,
|
||||
"types": [
|
||||
"node",
|
||||
"jest"
|
||||
] /* Specify type package names to be included without being referenced in a source file. */,
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "build/dist" /* Specify an output folder for all emitted files. */,
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
"noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied `any` type.. */,
|
||||
"strictNullChecks": true /* When type checking, take into account `null` and `undefined`. */,
|
||||
"strictFunctionTypes": true /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */,
|
||||
"strictBindCallApply": true /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */,
|
||||
"strictPropertyInitialization": true /* Check for class properties that are declared but not set in the constructor. */,
|
||||
"noImplicitThis": true /* Enable error reporting when `this` is given the type `any`. */,
|
||||
"useUnknownInCatchVariables": true /* Type catch clause variables as 'unknown' instead of 'any'. */,
|
||||
"alwaysStrict": true /* Ensure 'use strict' is always emitted. */,
|
||||
"noUnusedLocals": true /* Enable error reporting when a local variables aren't read. */,
|
||||
"noUnusedParameters": true /* Raise an error when a function parameter isn't read */,
|
||||
"exactOptionalPropertyTypes": true /* Interpret optional property types as written, rather than adding 'undefined'. */,
|
||||
"noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */,
|
||||
"noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */,
|
||||
"noUncheckedIndexedAccess": true /* Include 'undefined' in index signature results */,
|
||||
"noImplicitOverride": true /* Ensure overriding members in derived classes are marked with an override modifier. */,
|
||||
"noPropertyAccessFromIndexSignature": true /* Enforces using indexed accessors for keys declared using an indexed type */,
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.7.3-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -34,6 +34,7 @@ import React, {
|
||||
useState,
|
||||
} from "react";
|
||||
import useAsyncEffect from "use-async-effect";
|
||||
import StatusPage from "Common/Models/DatabaseModels/StatusPage";
|
||||
|
||||
export interface ComponentProps {
|
||||
children: ReactElement | Array<ReactElement>;
|
||||
@@ -58,6 +59,8 @@ const DashboardMasterPage: FunctionComponent<ComponentProps> = (
|
||||
const [headerHtml, setHeaderHtml] = useState<null | string>(null);
|
||||
const [footerHtml, setFooterHTML] = useState<null | string>(null);
|
||||
|
||||
const [statusPage, setStatusPage] = useState<StatusPage | null>(null);
|
||||
|
||||
const [hidePoweredByOneUptimeBranding, setHidePoweredByOneUptimeBranding] =
|
||||
useState<boolean>(false);
|
||||
|
||||
@@ -170,11 +173,16 @@ const DashboardMasterPage: FunctionComponent<ComponentProps> = (
|
||||
);
|
||||
setMasterPageData(response.data);
|
||||
|
||||
// set status page.
|
||||
const statusPage: StatusPage = BaseModel.fromJSONObject(
|
||||
(response.data["statusPage"] as JSONObject) || [],
|
||||
StatusPage,
|
||||
);
|
||||
|
||||
setStatusPage(statusPage);
|
||||
|
||||
// setfavicon.
|
||||
const favIcon: File | null = JSONFunctions.getJSONValueInPath(
|
||||
response.data || {},
|
||||
"statusPage.faviconFile",
|
||||
) as File | null;
|
||||
const favIcon: File | undefined = statusPage.faviconFile;
|
||||
if (favIcon && favIcon.file) {
|
||||
const link: any = document.createElement("link");
|
||||
link.rel = "icon";
|
||||
@@ -183,36 +191,23 @@ const DashboardMasterPage: FunctionComponent<ComponentProps> = (
|
||||
}
|
||||
|
||||
// setcss.
|
||||
const css: string | null = JSONFunctions.getJSONValueInPath(
|
||||
response.data || {},
|
||||
"statusPage.customCSS",
|
||||
) as string | null;
|
||||
|
||||
const css: string | null = statusPage.customCSS || null;
|
||||
if (css) {
|
||||
const style: any = document.createElement("style");
|
||||
style.innerText = css;
|
||||
(document as any).getElementsByTagName("head")[0].appendChild(style);
|
||||
}
|
||||
|
||||
const headHtml: string | null = JSONFunctions.getJSONValueInPath(
|
||||
response.data || {},
|
||||
"statusPage.headerHTML",
|
||||
) as string | null;
|
||||
const headHtml: string | null = statusPage.headerHTML || null;
|
||||
|
||||
const hidePoweredByOneUptimeBranding: boolean | null =
|
||||
JSONFunctions.getJSONValueInPath(
|
||||
response.data || {},
|
||||
"statusPage.hidePoweredByOneUptimeBranding",
|
||||
) as boolean | null;
|
||||
statusPage.hidePoweredByOneUptimeBranding || false;
|
||||
|
||||
setHidePoweredByOneUptimeBranding(
|
||||
Boolean(hidePoweredByOneUptimeBranding),
|
||||
);
|
||||
|
||||
const footHTML: string | null = JSONFunctions.getJSONValueInPath(
|
||||
response.data || {},
|
||||
"statusPage.footerHTML",
|
||||
) as string | null;
|
||||
const footHTML: string | null = statusPage.footerHTML || null;
|
||||
|
||||
if (headHtml) {
|
||||
setHeaderHtml(headHtml);
|
||||
@@ -314,6 +309,15 @@ const DashboardMasterPage: FunctionComponent<ComponentProps> = (
|
||||
isPreview={true}
|
||||
enableEmailSubscribers={props.enableEmailSubscribers}
|
||||
enableSMSSubscribers={props.enableSMSSubscribers}
|
||||
showIncidentsOnStatusPage={
|
||||
statusPage?.showIncidentsOnStatusPage || false
|
||||
}
|
||||
showAnnouncementsOnStatusPage={
|
||||
statusPage?.showAnnouncementsOnStatusPage || false
|
||||
}
|
||||
showScheduledMaintenanceEventsOnStatusPage={
|
||||
statusPage?.showScheduledMaintenanceEventsOnStatusPage || false
|
||||
}
|
||||
/>
|
||||
{props.children}
|
||||
{!footerHtml ? (
|
||||
|
||||
@@ -12,6 +12,9 @@ export interface ComponentProps {
|
||||
isPrivateStatusPage: boolean;
|
||||
enableEmailSubscribers: boolean;
|
||||
enableSMSSubscribers: boolean;
|
||||
showIncidentsOnStatusPage: boolean;
|
||||
showAnnouncementsOnStatusPage: boolean;
|
||||
showScheduledMaintenanceEventsOnStatusPage: boolean;
|
||||
}
|
||||
|
||||
const DashboardNavbar: FunctionComponent<ComponentProps> = (
|
||||
@@ -35,41 +38,53 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
|
||||
)}
|
||||
></NavBarItem>
|
||||
|
||||
<NavBarItem
|
||||
id="incidents-nav-bar-item"
|
||||
title="Incidents"
|
||||
icon={IconProp.Alert}
|
||||
exact={true}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
props.isPreview
|
||||
? (RouteMap[PageMap.PREVIEW_INCIDENT_LIST] as Route)
|
||||
: (RouteMap[PageMap.INCIDENT_LIST] as Route),
|
||||
)}
|
||||
></NavBarItem>
|
||||
{props.showIncidentsOnStatusPage ? (
|
||||
<NavBarItem
|
||||
id="incidents-nav-bar-item"
|
||||
title="Incidents"
|
||||
icon={IconProp.Alert}
|
||||
exact={true}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
props.isPreview
|
||||
? (RouteMap[PageMap.PREVIEW_INCIDENT_LIST] as Route)
|
||||
: (RouteMap[PageMap.INCIDENT_LIST] as Route),
|
||||
)}
|
||||
></NavBarItem>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<NavBarItem
|
||||
id="announcements-nav-bar-item"
|
||||
title="Announcements"
|
||||
icon={IconProp.Announcement}
|
||||
exact={true}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
props.isPreview
|
||||
? (RouteMap[PageMap.PREVIEW_ANNOUNCEMENT_LIST] as Route)
|
||||
: (RouteMap[PageMap.ANNOUNCEMENT_LIST] as Route),
|
||||
)}
|
||||
></NavBarItem>
|
||||
{props.showAnnouncementsOnStatusPage ? (
|
||||
<NavBarItem
|
||||
id="announcements-nav-bar-item"
|
||||
title="Announcements"
|
||||
icon={IconProp.Announcement}
|
||||
exact={true}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
props.isPreview
|
||||
? (RouteMap[PageMap.PREVIEW_ANNOUNCEMENT_LIST] as Route)
|
||||
: (RouteMap[PageMap.ANNOUNCEMENT_LIST] as Route),
|
||||
)}
|
||||
></NavBarItem>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<NavBarItem
|
||||
id="scheduled-events-nav-bar-item"
|
||||
title="Scheduled Events"
|
||||
icon={IconProp.Clock}
|
||||
exact={true}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
props.isPreview
|
||||
? (RouteMap[PageMap.PREVIEW_SCHEDULED_EVENT_LIST] as Route)
|
||||
: (RouteMap[PageMap.SCHEDULED_EVENT_LIST] as Route),
|
||||
)}
|
||||
></NavBarItem>
|
||||
{props.showScheduledMaintenanceEventsOnStatusPage ? (
|
||||
<NavBarItem
|
||||
id="scheduled-events-nav-bar-item"
|
||||
title="Scheduled Events"
|
||||
icon={IconProp.Clock}
|
||||
exact={true}
|
||||
route={RouteUtil.populateRouteParams(
|
||||
props.isPreview
|
||||
? (RouteMap[PageMap.PREVIEW_SCHEDULED_EVENT_LIST] as Route)
|
||||
: (RouteMap[PageMap.SCHEDULED_EVENT_LIST] as Route),
|
||||
)}
|
||||
></NavBarItem>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{props.enableEmailSubscribers || props.enableSMSSubscribers ? (
|
||||
<NavBarItem
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.7.3-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM public.ecr.aws/docker/library/node:21.7.3-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM public.ecr.aws/docker/library/node:21.2-alpine3.18
|
||||
FROM public.ecr.aws/docker/library/node:23.8-alpine3.21
|
||||
RUN mkdir /tmp/npm && chmod 2777 /tmp/npm && chown 1000:1000 /tmp/npm && npm config set cache /tmp/npm --global
|
||||
|
||||
RUN npm config set fetch-retries 5
|
||||
|
||||
@@ -36,7 +36,9 @@ RunCron(
|
||||
workflowStatus: WorkflowStatus.Error,
|
||||
logs: `${
|
||||
stalledWorkflowLog.logs
|
||||
} \n ${OneUptimeDate.getCurrentDateAsFormattedString()}: Workflow was not picked up by the runner and has timed out.`,
|
||||
} \n ${OneUptimeDate.getCurrentDateAsFormattedString({
|
||||
showSeconds: true,
|
||||
})}: Workflow was not picked up by the runner and has timed out.`,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user