mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 08:42:13 +02:00
Compare commits
123 Commits
admin-dash
...
7.0.778
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
864d0b3c00 | ||
|
|
14d22e5f12 | ||
|
|
3dd150692a | ||
|
|
c9b15dcfc7 | ||
|
|
b039a5a045 | ||
|
|
3040b21484 | ||
|
|
af15c6f5f5 | ||
|
|
4c82c922e2 | ||
|
|
08f48ad082 | ||
|
|
34a8ea806d | ||
|
|
a70e98f802 | ||
|
|
ee1ec87781 | ||
|
|
f21de699dd | ||
|
|
b30f9a472a | ||
|
|
e7c54b369d | ||
|
|
53bf92fac0 | ||
|
|
6da56df5b1 | ||
|
|
931cccf86a | ||
|
|
027966cae3 | ||
|
|
f568473588 | ||
|
|
ada26e3cce | ||
|
|
15e2c9cef2 | ||
|
|
a091cd4faa | ||
|
|
1fa5604cdd | ||
|
|
d9ed5f579e | ||
|
|
0138e98506 | ||
|
|
2feb024032 | ||
|
|
55bf11bfd1 | ||
|
|
05d6dd2182 | ||
|
|
3595f5bf6f | ||
|
|
398c08854a | ||
|
|
af8d85f6d2 | ||
|
|
296dfd15d5 | ||
|
|
efc446edf1 | ||
|
|
8453d32a4f | ||
|
|
41a8ddb09a | ||
|
|
9f5fa3542a | ||
|
|
b801aba506 | ||
|
|
c6e5d642b5 | ||
|
|
002abb7498 | ||
|
|
0e141b9b1a | ||
|
|
230ccc4144 | ||
|
|
ac4d2cc9ec | ||
|
|
a1f12fd14a | ||
|
|
3e4ad34179 | ||
|
|
a21bde486b | ||
|
|
4adb2b58ca | ||
|
|
929c39dea7 | ||
|
|
206c7d9bf1 | ||
|
|
e16c9cb3b7 | ||
|
|
542fb4355e | ||
|
|
f63b910d78 | ||
|
|
8254b635fb | ||
|
|
dfa85982c2 | ||
|
|
8451f9f90d | ||
|
|
c447941755 | ||
|
|
112e2e4faa | ||
|
|
fed9ab4621 | ||
|
|
18461d58d6 | ||
|
|
11095fc0bc | ||
|
|
a567cee47f | ||
|
|
158e2abc12 | ||
|
|
ca2095e867 | ||
|
|
708a9ba4b8 | ||
|
|
91a60eabbb | ||
|
|
7f0e07bd40 | ||
|
|
01b17b9dff | ||
|
|
b7e5cf78b9 | ||
|
|
d92d8c260b | ||
|
|
0576199db8 | ||
|
|
6f5a804d77 | ||
|
|
2f6420152e | ||
|
|
1d1d11bee2 | ||
|
|
594c36a512 | ||
|
|
ec5c852175 | ||
|
|
01ff01029b | ||
|
|
bf5d16b64c | ||
|
|
26ee469467 | ||
|
|
b514c8c189 | ||
|
|
2bbac9a545 | ||
|
|
03386eeba0 | ||
|
|
a09842c8b0 | ||
|
|
847b75b555 | ||
|
|
c839317283 | ||
|
|
1f4fd86845 | ||
|
|
8ed8c6a05c | ||
|
|
a716d54cc6 | ||
|
|
668d00418d | ||
|
|
5c36fa851c | ||
|
|
4352ada83e | ||
|
|
c6d76c4bb0 | ||
|
|
87a1a84d2e | ||
|
|
b02e622fd3 | ||
|
|
ac7e6b915f | ||
|
|
f3e1dccfc1 | ||
|
|
8683ac7677 | ||
|
|
eccb65f930 | ||
|
|
c1c27a387c | ||
|
|
aece287747 | ||
|
|
d6ff2c12fb | ||
|
|
50c8fe003d | ||
|
|
3e6b16fcf6 | ||
|
|
ce43514ee3 | ||
|
|
8318f09e26 | ||
|
|
7711902edd | ||
|
|
94ffa754eb | ||
|
|
48035ddec0 | ||
|
|
7694abe05e | ||
|
|
8eac47c4f9 | ||
|
|
fe90b50862 | ||
|
|
e3f2eaa3c6 | ||
|
|
fc09c689bc | ||
|
|
faf04a726c | ||
|
|
31e04a26ff | ||
|
|
90ea8ebee9 | ||
|
|
4d2e66fce3 | ||
|
|
6057fafd97 | ||
|
|
57671c444c | ||
|
|
11a3111098 | ||
|
|
e74c711dfd | ||
|
|
4f64693550 | ||
|
|
2336961178 | ||
|
|
c7938f62ae |
16
.github/workflows/common-jobs.yaml
vendored
16
.github/workflows/common-jobs.yaml
vendored
@@ -8,7 +8,21 @@ on:
|
||||
- 'release'
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
helm-lint:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Helm
|
||||
run: |
|
||||
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
- name: Lint Helm Chart
|
||||
run: |
|
||||
helm lint ./HelmChart/public/oneuptime
|
||||
|
||||
js-lint:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI_PIPELINE_ID: ${{github.run_number}}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -88,3 +88,5 @@ Backups/*.tar
|
||||
|
||||
Haraka/dkim/keys/private_base64.txt
|
||||
Haraka/dkim/keys/public_base64.txt
|
||||
|
||||
.eslintcache
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:current-alpine
|
||||
FROM node:current-alpine AS base
|
||||
USER root
|
||||
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-retry-maxtimeout 6000000
|
||||
RUN npm config set fetch-retry-mintimeout 1000000
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
|
||||
@@ -24,7 +27,8 @@ SHELL ["/bin/bash", "-c"]
|
||||
RUN mkdir /usr/src
|
||||
|
||||
# Install common
|
||||
RUN mkdir /usr/src/Common
|
||||
|
||||
FROM base AS common
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
RUN npm install
|
||||
@@ -33,7 +37,8 @@ COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
# Install Model
|
||||
RUN mkdir /usr/src/Model
|
||||
|
||||
FROM base AS model
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
RUN npm install
|
||||
@@ -42,7 +47,8 @@ COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
# Install CommonServer
|
||||
RUN mkdir /usr/src/CommonServer
|
||||
|
||||
FROM base AS commonserver
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
RUN npm install
|
||||
@@ -52,7 +58,8 @@ COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
# Install CommonUI
|
||||
RUN mkdir /usr/src/CommonUI
|
||||
|
||||
FROM base AS commonui
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY ./CommonUI/package*.json /usr/src/CommonUI/
|
||||
RUN npm install --force
|
||||
@@ -61,11 +68,25 @@ COPY ./CommonUI /usr/src/CommonUI
|
||||
|
||||
|
||||
#SET ENV Variables
|
||||
# Install app
|
||||
FROM base AS app
|
||||
|
||||
WORKDIR /usr/src/Common
|
||||
COPY --from=common /usr/src/Common .
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY --from=model /usr/src/Model .
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY --from=commonserver /usr/src/CommonServer .
|
||||
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY --from=commonui /usr/src/CommonUI .
|
||||
|
||||
|
||||
ENV PRODUCTION=true
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
|
||||
@@ -21,7 +21,7 @@ export default abstract class LoginUtil {
|
||||
UserUtil.setAccessToken(token);
|
||||
UserUtil.setEmail(user.email as Email);
|
||||
UserUtil.setUserId(user.id as ObjectID);
|
||||
UserUtil.setName(user.name as Name);
|
||||
UserUtil.setName(user.name || new Name(''));
|
||||
UserUtil.setIsMasterAdmin(user.isMasterAdmin as boolean);
|
||||
|
||||
Analytics.userAuth(user.email!);
|
||||
|
||||
@@ -2,13 +2,13 @@ const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const dotenv = require('dotenv');
|
||||
const express = require('express');
|
||||
const axios = require('axios');
|
||||
|
||||
const readEnvFile = async (pathToFile) => {
|
||||
const readEnvFile = (pathToFile) => {
|
||||
|
||||
const parsed = dotenv.config({ path: pathToFile }).parsed;
|
||||
|
||||
const env = {};
|
||||
const env = {
|
||||
};
|
||||
|
||||
for (const key in parsed) {
|
||||
env[key] = JSON.stringify(parsed[key]);
|
||||
@@ -17,67 +17,62 @@ const readEnvFile = async (pathToFile) => {
|
||||
return env;
|
||||
}
|
||||
|
||||
const webpackConfig = async () => {
|
||||
return {
|
||||
entry: "./src/Index.tsx",
|
||||
mode: "development",
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
path: path.resolve(__dirname, "public", "dist"),
|
||||
publicPath: "/accounts/dist/",
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json', '.css', '.scss'],
|
||||
alias: {
|
||||
react: path.resolve('./node_modules/react'),
|
||||
module.exports = {
|
||||
entry: "./src/Index.tsx",
|
||||
mode: "development",
|
||||
output: {
|
||||
filename: "bundle.js",
|
||||
path: path.resolve(__dirname, "public", "dist"),
|
||||
publicPath: "/accounts/dist/",
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json', '.css', '.scss'],
|
||||
alias: {
|
||||
react: path.resolve('./node_modules/react'),
|
||||
}
|
||||
},
|
||||
externals: {
|
||||
'react-native-sqlite-storage': 'react-native-sqlite-storage'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process': {
|
||||
'env': {
|
||||
...readEnvFile('/usr/src/app/dev-env/.env')
|
||||
}
|
||||
}
|
||||
},
|
||||
externals: {
|
||||
'react-native-sqlite-storage': 'react-native-sqlite-storage'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process': {
|
||||
'env': {
|
||||
...(await readEnvFile('/usr/src/app/dev-env/.env'))
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
use: 'ts-loader'
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: ['style-loader', 'css-loader', "sass-loader"]
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
loader: 'file-loader'
|
||||
}
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
devMiddleware: {
|
||||
writeToDisk: true,
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
use: 'ts-loader'
|
||||
},
|
||||
allowedHosts: "all",
|
||||
setupMiddlewares: (middlewares, devServer) => {
|
||||
devServer.app.use('/accounts/assets', express.static(path.resolve(__dirname, 'public', 'assets')));
|
||||
return middlewares;
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: ['style-loader', 'css-loader', "sass-loader"]
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||
loader: 'file-loader'
|
||||
}
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
devMiddleware: {
|
||||
writeToDisk: true,
|
||||
},
|
||||
devtool: 'eval-source-map',
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = webpackConfig;
|
||||
allowedHosts: "all",
|
||||
setupMiddlewares: (middlewares, devServer) => {
|
||||
devServer.app.use('/accounts/assets', express.static(path.resolve(__dirname, 'public', 'assets')));
|
||||
return middlewares;
|
||||
}
|
||||
},
|
||||
devtool: 'eval-source-map',
|
||||
}
|
||||
@@ -3,10 +3,13 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:current-alpine
|
||||
FROM node:current-alpine AS base
|
||||
USER root
|
||||
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-retry-maxtimeout 6000000
|
||||
RUN npm config set fetch-retry-mintimeout 1000000
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
|
||||
@@ -24,7 +27,8 @@ SHELL ["/bin/bash", "-c"]
|
||||
RUN mkdir /usr/src
|
||||
|
||||
# Install common
|
||||
RUN mkdir /usr/src/Common
|
||||
|
||||
FROM base AS common
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
RUN npm install
|
||||
@@ -32,7 +36,8 @@ COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
# Install Model
|
||||
RUN mkdir /usr/src/Model
|
||||
|
||||
FROM base AS model
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
RUN npm install
|
||||
@@ -41,7 +46,8 @@ COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
# Install CommonServer
|
||||
RUN mkdir /usr/src/CommonServer
|
||||
|
||||
FROM base AS commonserver
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
RUN npm install
|
||||
@@ -51,7 +57,8 @@ COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
# Install CommonUI
|
||||
RUN mkdir /usr/src/CommonUI
|
||||
|
||||
FROM base AS commonui
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY ./CommonUI/package*.json /usr/src/CommonUI/
|
||||
RUN npm install --force
|
||||
@@ -59,11 +66,25 @@ COPY ./CommonUI /usr/src/CommonUI
|
||||
|
||||
|
||||
#SET ENV Variables
|
||||
# Install app
|
||||
FROM base AS app
|
||||
|
||||
WORKDIR /usr/src/Common
|
||||
COPY --from=common /usr/src/Common .
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY --from=model /usr/src/Model .
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY --from=commonserver /usr/src/CommonServer .
|
||||
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY --from=commonui /usr/src/CommonUI .
|
||||
|
||||
|
||||
ENV PRODUCTION=true
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
|
||||
@@ -24,6 +24,7 @@ import SettingsEmail from './Pages/Settings/SMTP/Index';
|
||||
import SettingsCallSMS from './Pages/Settings/CallSMS/Index';
|
||||
import SettingsProbes from './Pages/Settings/Probes/Index';
|
||||
import SettingsAuthentication from './Pages/Settings/Authentication/Index';
|
||||
import SettingsAPIKey from './Pages/Settings/APIKey/Index';
|
||||
|
||||
const App: () => JSX.Element = () => {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
@@ -105,6 +106,11 @@ const App: () => JSX.Element = () => {
|
||||
}
|
||||
element={<SettingsAuthentication />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_API_KEY]?.toString() || ''}
|
||||
element={<SettingsAPIKey />}
|
||||
/>
|
||||
</Routes>
|
||||
</MasterPage>
|
||||
);
|
||||
|
||||
102
AdminDashboard/src/Pages/Settings/APIKey/Index.tsx
Normal file
102
AdminDashboard/src/Pages/Settings/APIKey/Index.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import DashboardSideMenu from '../SideMenu';
|
||||
import GlobalConfig from 'Model/Models/GlobalConfig';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<Page
|
||||
title={'Admin Settings'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Admin Dashboard',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'API Key',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_HOST] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
<CardModelDetail
|
||||
name="API Key Settings"
|
||||
cardProps={{
|
||||
title: 'Master API Key Settings',
|
||||
description:
|
||||
'This API key has root access to all the resources in all the projects on OneUptime.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit API Key Settings"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
masterApiKey: true,
|
||||
},
|
||||
title: 'Master API Key',
|
||||
fieldType: FormFieldSchemaType.ObjectID,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isMasterApiKeyEnabled: true,
|
||||
},
|
||||
title: 'Enabled',
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
masterApiKey: true,
|
||||
},
|
||||
title: 'Master API Key',
|
||||
description:
|
||||
'This API key has root access to all the resources in all the projects on OneUptime.',
|
||||
fieldType: FieldType.HiddenText,
|
||||
opts: {
|
||||
isCopyable: true,
|
||||
},
|
||||
placeholder: 'API Key not generated yet.',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isMasterApiKeyEnabled: true,
|
||||
},
|
||||
title: 'Enabled',
|
||||
description:
|
||||
'Enable or disable the master API key. If disabled, all requests using this key will fail.',
|
||||
fieldType: FieldType.Boolean,
|
||||
placeholder: 'Not Enabled',
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
@@ -47,20 +47,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
isEditable={true}
|
||||
editButtonText="Edit Host"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
host: true,
|
||||
},
|
||||
title: 'Host',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description:
|
||||
'IP address or Hostname of this server instance.',
|
||||
placeholder: 'oneuptime.yourcompany.com',
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
useHttps: true,
|
||||
@@ -76,15 +62,6 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
host: true,
|
||||
},
|
||||
title: 'Host',
|
||||
placeholder: 'None',
|
||||
description:
|
||||
'IP address or Hostname of this server instance.',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
useHttps: true,
|
||||
|
||||
@@ -2,15 +2,65 @@ import Route from 'Common/Types/API/Route';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from 'react';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import DashboardSideMenu from '../SideMenu';
|
||||
import GlobalConfig from 'Model/Models/GlobalConfig';
|
||||
import GlobalConfig, { EmailServerType } from 'Model/Models/GlobalConfig';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import ModelAPI from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import PageLoader from 'CommonUI/src/Components/Loader/PageLoader';
|
||||
import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
|
||||
import Pill from 'CommonUI/src/Components/Pill/Pill';
|
||||
import { Green, Red } from 'Common/Types/BrandColors';
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
const [emailServerType, setemailServerType] =
|
||||
React.useState<EmailServerType>(EmailServerType.Internal);
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||
|
||||
const [error, setError] = React.useState<string>('');
|
||||
|
||||
const fetchItem: Function = async (): Promise<void> => {
|
||||
setIsLoading(true);
|
||||
|
||||
const globalConfig: GlobalConfig | null =
|
||||
await ModelAPI.getItem<GlobalConfig>(
|
||||
GlobalConfig,
|
||||
ObjectID.getZeroObjectID(),
|
||||
{
|
||||
_id: true,
|
||||
emailServerType: true,
|
||||
}
|
||||
);
|
||||
|
||||
if (globalConfig) {
|
||||
setemailServerType(
|
||||
globalConfig.emailServerType || EmailServerType.Internal
|
||||
);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchItem().catch((err: Error) => {
|
||||
setError(err.message);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Page
|
||||
title={'Admin Settings'}
|
||||
@@ -37,102 +87,31 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* Project Settings View */}
|
||||
|
||||
<CardModelDetail
|
||||
name="Host Settings"
|
||||
name="Internal SMTP Settings"
|
||||
cardProps={{
|
||||
title: 'Email and SMTP Settings',
|
||||
title: 'Email Server Settings',
|
||||
description:
|
||||
'Email and SMTP Settings. We will use this SMTP server to send all the emails.',
|
||||
'Pick which email server you would like to use to send emails.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit SMTP Config"
|
||||
formSteps={[
|
||||
{
|
||||
title: 'SMTP Server',
|
||||
id: 'server-info',
|
||||
},
|
||||
{
|
||||
title: 'Authentication',
|
||||
id: 'authentication',
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
id: 'email-info',
|
||||
},
|
||||
]}
|
||||
editButtonText="Edit Server"
|
||||
onSaveSuccess={() => {
|
||||
window.location.reload();
|
||||
}}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
smtpHost: true,
|
||||
emailServerType: true,
|
||||
},
|
||||
title: 'Hostname',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Hostname,
|
||||
title: 'Email Server Type',
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
dropdownOptions:
|
||||
DropdownUtil.getDropdownOptionsFromEnum(
|
||||
EmailServerType
|
||||
),
|
||||
required: true,
|
||||
placeholder: 'smtp.server.com',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPort: true,
|
||||
},
|
||||
title: 'Port',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Port,
|
||||
required: true,
|
||||
placeholder: '587',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isSMTPSecure: true,
|
||||
},
|
||||
title: 'Use SSL / TLS',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
description: 'Make email communication secure?',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpUsername: true,
|
||||
},
|
||||
title: 'Username',
|
||||
stepId: 'authentication',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: 'emailuser',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPassword: true,
|
||||
},
|
||||
title: 'Password',
|
||||
stepId: 'authentication',
|
||||
fieldType: FormFieldSchemaType.EncryptedText,
|
||||
required: false,
|
||||
placeholder: 'Password',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromEmail: true,
|
||||
},
|
||||
title: 'Email From',
|
||||
stepId: 'email-info',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
description:
|
||||
'This is the display email your team and customers see, when they receive emails from OneUptime.',
|
||||
placeholder: 'email@company.com',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromName: true,
|
||||
},
|
||||
title: 'From Name',
|
||||
stepId: 'email-info',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description:
|
||||
'This is the display name your team and customers see, when they receive emails from OneUptime.',
|
||||
placeholder: 'Company, Inc.',
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
@@ -141,53 +120,269 @@ const Settings: FunctionComponent = (): ReactElement => {
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
smtpHost: true,
|
||||
emailServerType: true,
|
||||
},
|
||||
title: 'SMTP Host',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPort: true,
|
||||
},
|
||||
title: 'SMTP Port',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpUsername: true,
|
||||
},
|
||||
title: 'SMTP Username',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromEmail: true,
|
||||
},
|
||||
title: 'SMTP Email',
|
||||
placeholder: 'None',
|
||||
fieldType: FieldType.Email,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromName: true,
|
||||
},
|
||||
title: 'SMTP From Name',
|
||||
placeholder: 'None',
|
||||
},
|
||||
|
||||
{
|
||||
field: {
|
||||
isSMTPSecure: true,
|
||||
},
|
||||
title: 'Use SSL/TLS',
|
||||
placeholder: 'No',
|
||||
fieldType: FieldType.Boolean,
|
||||
title: 'Email Server Type',
|
||||
fieldType: FieldType.Text,
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
|
||||
{emailServerType === EmailServerType.CustomSMTP ? (
|
||||
<CardModelDetail
|
||||
name="Host Settings"
|
||||
cardProps={{
|
||||
title: 'Custom Email and SMTP Settings',
|
||||
description:
|
||||
'If you have not enabled Internal SMTP server to send emails. Please configure your SMTP server here.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit SMTP Config"
|
||||
formSteps={[
|
||||
{
|
||||
title: 'SMTP Server',
|
||||
id: 'server-info',
|
||||
},
|
||||
{
|
||||
title: 'Authentication',
|
||||
id: 'authentication',
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
id: 'email-info',
|
||||
},
|
||||
]}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
smtpHost: true,
|
||||
},
|
||||
title: 'Hostname',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Hostname,
|
||||
required: true,
|
||||
placeholder: 'smtp.server.com',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPort: true,
|
||||
},
|
||||
title: 'Port',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Port,
|
||||
required: true,
|
||||
placeholder: '587',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isSMTPSecure: true,
|
||||
},
|
||||
title: 'Use SSL / TLS',
|
||||
stepId: 'server-info',
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
description: 'Make email communication secure?',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpUsername: true,
|
||||
},
|
||||
title: 'Username',
|
||||
stepId: 'authentication',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: 'emailuser',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPassword: true,
|
||||
},
|
||||
title: 'Password',
|
||||
stepId: 'authentication',
|
||||
fieldType: FormFieldSchemaType.EncryptedText,
|
||||
required: false,
|
||||
placeholder: 'Password',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromEmail: true,
|
||||
},
|
||||
title: 'Email From',
|
||||
stepId: 'email-info',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
description:
|
||||
'This is the display email your team and customers see, when they receive emails from OneUptime.',
|
||||
placeholder: 'email@company.com',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromName: true,
|
||||
},
|
||||
title: 'From Name',
|
||||
stepId: 'email-info',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
description:
|
||||
'This is the display name your team and customers see, when they receive emails from OneUptime.',
|
||||
placeholder: 'Company, Inc.',
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
smtpHost: true,
|
||||
},
|
||||
title: 'SMTP Host',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpPort: true,
|
||||
},
|
||||
title: 'SMTP Port',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpUsername: true,
|
||||
},
|
||||
title: 'SMTP Username',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromEmail: true,
|
||||
},
|
||||
title: 'SMTP Email',
|
||||
placeholder: 'None',
|
||||
fieldType: FieldType.Email,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
smtpFromName: true,
|
||||
},
|
||||
title: 'SMTP From Name',
|
||||
placeholder: 'None',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isSMTPSecure: true,
|
||||
},
|
||||
title: 'Use SSL/TLS',
|
||||
placeholder: 'No',
|
||||
fieldType: FieldType.Boolean,
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{emailServerType === EmailServerType.Sendgrid ? (
|
||||
<CardModelDetail
|
||||
name="Sendgrid Settings"
|
||||
cardProps={{
|
||||
title: 'Sendgrid Settings',
|
||||
description:
|
||||
'Enter your Sendgrid API key to send emails through Sendgrid.',
|
||||
}}
|
||||
isEditable={true}
|
||||
editButtonText="Edit API Key"
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
sendgridApiKey: true,
|
||||
},
|
||||
title: 'Sendgrid API Key',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: 'Sendgrid API Key',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
sendgridFromEmail: true,
|
||||
},
|
||||
title: 'From Email',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
placeholder: 'email@yourcompany.com',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
sendgridFromName: true,
|
||||
},
|
||||
title: 'From Name',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: 'Acme, Inc.',
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: GlobalConfig,
|
||||
id: 'model-detail-global-config',
|
||||
selectMoreFields: {
|
||||
sendgridFromEmail: true,
|
||||
sendgridFromName: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
sendgridApiKey: true,
|
||||
},
|
||||
title: '',
|
||||
placeholder: 'None',
|
||||
getElement: (item: JSONObject) => {
|
||||
if (
|
||||
item['sendgridApiKey'] &&
|
||||
item['sendgridFromEmail'] &&
|
||||
item['sendgridFromName']
|
||||
) {
|
||||
return (
|
||||
<Pill
|
||||
text="Enabled"
|
||||
color={Green}
|
||||
/>
|
||||
);
|
||||
} else if (!item['sendgridApiKey']) {
|
||||
return (
|
||||
<Pill
|
||||
text="Not Enabled. Please add the API key."
|
||||
color={Red}
|
||||
/>
|
||||
);
|
||||
} else if (!item['sendgridFromEmail']) {
|
||||
return (
|
||||
<Pill
|
||||
text="Not Enabled. Please add the From Email."
|
||||
color={Red}
|
||||
/>
|
||||
);
|
||||
} else if (!item['sendgridFromName']) {
|
||||
return (
|
||||
<Pill
|
||||
text="Not Enabled. Please add the From Name."
|
||||
color={Red}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <></>;
|
||||
},
|
||||
},
|
||||
],
|
||||
modelId: ObjectID.getZeroObjectID(),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -63,6 +63,17 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
|
||||
icon={IconProp.Signal}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
<SideMenuSection title="API and Integrations">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'API Key',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_API_KEY] as Route
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Code}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
</SideMenu>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ enum PageMap {
|
||||
SETTINGS_CALL_AND_SMS = 'SETTINGS_CALL_AND_SMS',
|
||||
SETTINGS_PROBES = 'SETTINGS_PROBES',
|
||||
SETTINGS_AUTHENTICATION = 'SETTINGS_AUTHENTICATION',
|
||||
SETTINGS_API_KEY = 'SETTINGS_API_KEY',
|
||||
}
|
||||
|
||||
export default PageMap;
|
||||
|
||||
@@ -18,6 +18,7 @@ const RouteMap: Dictionary<Route> = {
|
||||
[PageMap.SETTINGS_AUTHENTICATION]: new Route(
|
||||
`/admin/settings/authentication`
|
||||
),
|
||||
[PageMap.SETTINGS_API_KEY]: new Route(`/admin/settings/api-key`),
|
||||
};
|
||||
|
||||
export class RouteUtil {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Pull base image nodejs image.
|
||||
FROM node:current-alpine
|
||||
FROM node:current-alpine AS base
|
||||
USER root
|
||||
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-retry-maxtimeout 6000000
|
||||
RUN npm config set fetch-retry-mintimeout 1000000
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
|
||||
@@ -20,7 +23,8 @@ SHELL ["/bin/bash", "-c"]
|
||||
RUN mkdir /usr/src
|
||||
|
||||
# Install common
|
||||
RUN mkdir /usr/src/Common
|
||||
|
||||
FROM base AS common
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
RUN npm install
|
||||
@@ -28,7 +32,8 @@ COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
# Install Model
|
||||
RUN mkdir /usr/src/Model
|
||||
|
||||
FROM base AS model
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
RUN npm install
|
||||
@@ -37,7 +42,8 @@ COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
# Install CommonServer
|
||||
RUN mkdir /usr/src/CommonServer
|
||||
|
||||
FROM base AS commonserver
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
RUN npm install
|
||||
@@ -45,9 +51,21 @@ COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
# Install app
|
||||
FROM base AS app
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/Common
|
||||
COPY --from=common /usr/src/Common .
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY --from=model /usr/src/Model .
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY --from=commonserver /usr/src/CommonServer .
|
||||
|
||||
|
||||
ENV PRODUCTION=true
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
|
||||
@@ -229,6 +229,9 @@ export default class ServiceHandler {
|
||||
DashboardApiRoute.toString() +
|
||||
currentResource.model.crudApiPath?.toString();
|
||||
|
||||
pageData.isMasterAdminApiDocs =
|
||||
currentResource.model.isMasterAdminApiDocs;
|
||||
|
||||
return res.render('pages/index', {
|
||||
page: page,
|
||||
resources: Resources,
|
||||
|
||||
@@ -2,6 +2,7 @@ import BaseModel from 'Common/Models/BaseModel';
|
||||
import Models from 'Model/Models/Index';
|
||||
import ArrayUtil from 'Common/Types/ArrayUtil';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import { IsBillingEnabled } from 'CommonServer/EnvironmentConfig';
|
||||
|
||||
export interface ModelDocumentation {
|
||||
name: string;
|
||||
@@ -15,7 +16,13 @@ export default class ResourceUtil {
|
||||
const resources: Array<ModelDocumentation> = Models.filter(
|
||||
(model: typeof BaseModel) => {
|
||||
const modelInstance: BaseModel = new model();
|
||||
return modelInstance.enableDocumentation;
|
||||
let showDocs: boolean = modelInstance.enableDocumentation;
|
||||
|
||||
if (modelInstance.isMasterAdminApiDocs && IsBillingEnabled) {
|
||||
showDocs = false;
|
||||
}
|
||||
|
||||
return showDocs;
|
||||
}
|
||||
)
|
||||
.map((model: typeof BaseModel) => {
|
||||
|
||||
483
ApiReference/package-lock.json
generated
483
ApiReference/package-lock.json
generated
@@ -37,7 +37,6 @@
|
||||
"nodemon": "^2.0.20",
|
||||
"npm-force-resolutions": "0.0.10",
|
||||
"ora": "^6.1.0",
|
||||
"puppeteer": "^13.5.1",
|
||||
"start-server-and-test": "^1.14.0",
|
||||
"ts-jest": "^28.0.2",
|
||||
"ts-node-dev": "^1.1.8",
|
||||
@@ -59,7 +58,7 @@
|
||||
"moment-timezone": "^0.5.40",
|
||||
"nanoid": "^3.3.2",
|
||||
"nanoid-dictionary": "^4.3.0",
|
||||
"posthog-js": "^1.37.0",
|
||||
"posthog-js": "^1.77.0",
|
||||
"process": "^0.11.10",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"slugify": "^1.6.5",
|
||||
@@ -67,7 +66,7 @@
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^6.3.1",
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^17.0.22",
|
||||
"jest": "^27.5.1",
|
||||
@@ -79,6 +78,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@clickhouse/client": "^0.2.1",
|
||||
"@elastic/elasticsearch": "^8.1.0",
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.31.0",
|
||||
@@ -811,6 +811,21 @@
|
||||
"node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/console/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/console/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -827,6 +842,24 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/console/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/console/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jest/console/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -896,6 +929,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/core/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/core/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -912,6 +960,24 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/core/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/core/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jest/core/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -1096,6 +1162,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/reporters/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/reporters/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -1112,6 +1193,24 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/reporters/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/reporters/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jest/reporters/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -1333,6 +1432,24 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/types/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/types/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jest/types/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -2758,7 +2875,8 @@
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/colors": {
|
||||
"version": "1.0.3",
|
||||
@@ -2841,6 +2959,11 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/compression/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/compression/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
@@ -3549,6 +3672,11 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/extract-zip": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
@@ -4395,6 +4523,11 @@
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jake/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/jake/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -5529,6 +5662,21 @@
|
||||
"node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-resolve/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-resolve/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -5545,6 +5693,24 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-resolve/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-resolve/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jest-resolve/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -6026,6 +6192,12 @@
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-util/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jest-util/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -6478,6 +6650,20 @@
|
||||
"integrity": "sha512-vlCUxxQAB8Nu6LQHqPpDRiMi06Du593/my/6JbMttQeEfJ7pf4OS8obSTh5xSOS80U/O7fq59Q8rQGAUxQatUQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lighthouse/node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/lighthouse/node_modules/semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
@@ -6769,7 +6955,8 @@
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/multimatch": {
|
||||
"version": "5.0.0",
|
||||
@@ -7453,31 +7640,6 @@
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/puppeteer": {
|
||||
"version": "13.7.0",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-13.7.0.tgz",
|
||||
"integrity": "sha512-U1uufzBjz3+PkpCxFrWzh4OrMIdIb2ztzCu0YEPfRHjHswcSwHZswnK+WdsOQJsRV8WeTg3jLhJR4D867+fjsA==",
|
||||
"deprecated": "< 18.1.0 is no longer supported",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"cross-fetch": "3.1.5",
|
||||
"debug": "4.3.4",
|
||||
"devtools-protocol": "0.0.981744",
|
||||
"extract-zip": "2.0.1",
|
||||
"https-proxy-agent": "5.0.1",
|
||||
"pkg-dir": "4.2.0",
|
||||
"progress": "2.0.3",
|
||||
"proxy-from-env": "1.1.0",
|
||||
"rimraf": "3.0.2",
|
||||
"tar-fs": "2.1.1",
|
||||
"unbzip2-stream": "1.4.3",
|
||||
"ws": "8.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/puppeteer-core": {
|
||||
"version": "13.7.0",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.7.0.tgz",
|
||||
@@ -7537,42 +7699,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/puppeteer/node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/puppeteer/node_modules/ws": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
|
||||
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
@@ -8937,6 +9063,24 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
@@ -9625,6 +9769,15 @@
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -9635,6 +9788,21 @@
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -9689,6 +9857,15 @@
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -9699,6 +9876,21 @@
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -9840,6 +10032,15 @@
|
||||
"v8-to-istanbul": "^9.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -9850,6 +10051,21 @@
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -10018,6 +10234,21 @@
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -11165,7 +11396,8 @@
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.0.3",
|
||||
@@ -11190,7 +11422,7 @@
|
||||
"Common": {
|
||||
"version": "file:../Common",
|
||||
"requires": {
|
||||
"@faker-js/faker": "^6.3.1",
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/nanoid-dictionary": "^4.2.0",
|
||||
@@ -11204,7 +11436,7 @@
|
||||
"moment-timezone": "^0.5.40",
|
||||
"nanoid": "^3.3.2",
|
||||
"nanoid-dictionary": "^4.3.0",
|
||||
"posthog-js": "^1.37.0",
|
||||
"posthog-js": "^1.77.0",
|
||||
"process": "^0.11.10",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"slugify": "^1.6.5",
|
||||
@@ -11216,6 +11448,7 @@
|
||||
"CommonServer": {
|
||||
"version": "file:../CommonServer",
|
||||
"requires": {
|
||||
"@clickhouse/client": "^0.2.1",
|
||||
"@elastic/elasticsearch": "^8.1.0",
|
||||
"@faker-js/faker": "^6.3.1",
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
@@ -11297,6 +11530,11 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
@@ -11861,6 +12099,11 @@
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12481,6 +12724,11 @@
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -13309,6 +13557,15 @@
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -13319,6 +13576,21 @@
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -13703,6 +13975,12 @@
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -14024,6 +14302,17 @@
|
||||
"yargs-parser": "^21.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
@@ -14296,7 +14585,8 @@
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"multimatch": {
|
||||
"version": "5.0.0",
|
||||
@@ -14781,44 +15071,6 @@
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"puppeteer": {
|
||||
"version": "13.7.0",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-13.7.0.tgz",
|
||||
"integrity": "sha512-U1uufzBjz3+PkpCxFrWzh4OrMIdIb2ztzCu0YEPfRHjHswcSwHZswnK+WdsOQJsRV8WeTg3jLhJR4D867+fjsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-fetch": "3.1.5",
|
||||
"debug": "4.3.4",
|
||||
"devtools-protocol": "0.0.981744",
|
||||
"extract-zip": "2.0.1",
|
||||
"https-proxy-agent": "5.0.1",
|
||||
"pkg-dir": "4.2.0",
|
||||
"progress": "2.0.3",
|
||||
"proxy-from-env": "1.1.0",
|
||||
"rimraf": "3.0.2",
|
||||
"tar-fs": "2.1.1",
|
||||
"unbzip2-stream": "1.4.3",
|
||||
"ws": "8.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
|
||||
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"puppeteer-core": {
|
||||
"version": "13.7.0",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.7.0.tgz",
|
||||
@@ -15857,6 +16109,21 @@
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
"nodemon": "^2.0.20",
|
||||
"npm-force-resolutions": "0.0.10",
|
||||
"ora": "^6.1.0",
|
||||
"puppeteer": "^13.5.1",
|
||||
|
||||
"start-server-and-test": "^1.14.0",
|
||||
"ts-jest": "^28.0.2",
|
||||
"ts-node-dev": "^1.1.8",
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
|
||||
|
||||
<h2>Permissions</h2>
|
||||
|
||||
<% if(!pageData.isMasterAdminApiDocs){ %>
|
||||
<p class="lead"> Your API Token needs permissions to create, update, read or delete this resource. If you do not have permissions to make a request a <code class="inline-code">4xx</code> status will be sent as response. </p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="consuming-webhooks" >
|
||||
Read Permissions
|
||||
@@ -138,6 +142,28 @@
|
||||
|
||||
</div>
|
||||
|
||||
<% } %>
|
||||
|
||||
<% if(pageData.isMasterAdminApiDocs){ %>
|
||||
|
||||
|
||||
<div class="border-l-4 border-yellow-400 bg-yellow-50 p-4">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<div class="text-sm font-medium text-yellow-700 mb-0">
|
||||
This API can only be accessed through a Master API Token. You can create one on the Admin Dashboard. Please add the token to the <code class="inline-code">ApiKey</code> header to make the request.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% } %>
|
||||
|
||||
|
||||
<h2 id="the-contact-model" class="scroll-mt-24">
|
||||
The <%= pageData.title -%> model
|
||||
|
||||
@@ -110,6 +110,7 @@ export default class BaseModel extends BaseEntity {
|
||||
public enableWorkflowOn!: EnableWorkflowOn;
|
||||
|
||||
public enableDocumentation!: boolean;
|
||||
public isMasterAdminApiDocs!: boolean;
|
||||
|
||||
public currentUserCanAccessColumnBy!: string | null;
|
||||
public labelsColumn!: string | null;
|
||||
|
||||
@@ -24,7 +24,7 @@ export const AccountsRoute: Route = new Route('/accounts');
|
||||
|
||||
export const WorkflowRoute: Route = new Route('/workflow');
|
||||
|
||||
export const ApiReferenceRoute: Route = new Route('/api-reference');
|
||||
export const ApiReferenceRoute: Route = new Route('/reference');
|
||||
|
||||
export const AdminDashboardRoute: Route = new Route('/admin');
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ export default class Hostname extends DatabaseProperty {
|
||||
}
|
||||
|
||||
public set hostname(value: string) {
|
||||
value = value.trim();
|
||||
|
||||
if (Hostname.isValid(value)) {
|
||||
this._route = value;
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const LIMIT_MAX: number = 10000;
|
||||
|
||||
export const LIMIT_PER_PROJECT: number = 300;
|
||||
|
||||
export const DEFAULT_LIMIT: number = 10;
|
||||
export default LIMIT_MAX;
|
||||
|
||||
@@ -7,6 +7,7 @@ enum ExceptionCode {
|
||||
WebRequestException = 6,
|
||||
BadDataException = 400,
|
||||
BadRequestException = 400,
|
||||
UnabletoReachServerException = 415,
|
||||
ServerException = 500,
|
||||
NotAuthorizedException = 403,
|
||||
NotAuthenticatedException = 401,
|
||||
|
||||
8
Common/Types/Exception/UnableToReachServer.ts
Normal file
8
Common/Types/Exception/UnableToReachServer.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import Exception from './Exception';
|
||||
import ExceptionCode from './ExceptionCode';
|
||||
|
||||
export default class UnableToReachServer extends Exception {
|
||||
public constructor(message: string) {
|
||||
super(ExceptionCode.UnabletoReachServerException, message);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
export default () => {
|
||||
export interface EnableDocumentationProps {
|
||||
isMasterAdminApiDocs?: boolean | undefined;
|
||||
}
|
||||
|
||||
export default (props?: EnableDocumentationProps | undefined) => {
|
||||
return (ctr: Function) => {
|
||||
ctr.prototype.enableDocumentation = true;
|
||||
ctr.prototype.isMasterAdminApiDocs =
|
||||
props?.isMasterAdminApiDocs || false;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -19,7 +19,10 @@ import DatabaseCommonInteractionProps from 'Common/Types/Database/DatabaseCommon
|
||||
import Query from '../Types/Database/Query';
|
||||
import Select from '../Types/Database/Select';
|
||||
import Sort from '../Types/Database/Sort';
|
||||
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import {
|
||||
DEFAULT_LIMIT,
|
||||
LIMIT_PER_PROJECT,
|
||||
} from 'Common/Types/Database/LimitMax';
|
||||
import PartialEntity from 'Common/Types/Database/PartialEntity';
|
||||
import { UserPermission } from 'Common/Types/Permission';
|
||||
import { IsBillingEnabled } from '../EnvironmentConfig';
|
||||
@@ -283,7 +286,7 @@ export default class BaseAPI<
|
||||
|
||||
const limit: PositiveNumber = req.query['limit']
|
||||
? new PositiveNumber(req.query['limit'] as string)
|
||||
: new PositiveNumber(10);
|
||||
: new PositiveNumber(DEFAULT_LIMIT);
|
||||
|
||||
if (limit.toNumber() > LIMIT_PER_PROJECT) {
|
||||
throw new BadRequestException(
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import Probe from 'Model/Models/Probe';
|
||||
import ProbeService, {
|
||||
Service as ProbeServiceType,
|
||||
} from '../Services/ProbeService';
|
||||
import {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
NextFunction,
|
||||
} from '../Utils/Express';
|
||||
import Response from '../Utils/Response';
|
||||
import BaseAPI from './BaseAPI';
|
||||
import GlobalConfigService from '../Services/GlobalConfigService';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import GlobalConfig from 'Model/Models/GlobalConfig';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
|
||||
export default class ProbeAPI extends BaseAPI<Probe, ProbeServiceType> {
|
||||
public constructor() {
|
||||
super(Probe, ProbeService);
|
||||
|
||||
this.router.get(
|
||||
`${new this.entityType().getCrudApiPath()?.toString()}/vars`,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
const globalConfig: GlobalConfig | null = await GlobalConfigService.findOneById({
|
||||
id: ObjectID.getZeroObjectID(),
|
||||
select: {
|
||||
host: true,
|
||||
useHttps: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if(!globalConfig){
|
||||
return Response.sendErrorResponse(req, res, new BadDataException("Global Config not found"));
|
||||
}
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, {
|
||||
HOST: globalConfig?.host?.toString() || 'localhost',
|
||||
USE_HTTPS: globalConfig?.useHttps || false,
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +1,46 @@
|
||||
import Probe from 'Model/Models/Probe';
|
||||
import ProbeService, {
|
||||
Service as ProbeServiceType,
|
||||
} from '../Services/ProbeService';
|
||||
import GlobalConfig from 'Model/Models/GlobalConfig';
|
||||
import GlobalConfigService, {
|
||||
Service as GlobalConfigServiceType,
|
||||
} from '../Services/GlobalConfigService';
|
||||
import {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
ExpressResponse,
|
||||
NextFunction,
|
||||
} from '../Utils/Express';
|
||||
import Response from '../Utils/Response';
|
||||
import BaseAPI from './BaseAPI';
|
||||
import GlobalConfigService from '../Services/GlobalConfigService';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
|
||||
export default class ProbeAPI extends BaseAPI<Probe, ProbeServiceType> {
|
||||
export default class GlobalConfigAPI extends BaseAPI<
|
||||
GlobalConfig,
|
||||
GlobalConfigServiceType
|
||||
> {
|
||||
public constructor() {
|
||||
super(Probe, ProbeService);
|
||||
super(GlobalConfig, GlobalConfigService);
|
||||
|
||||
this.router.get(
|
||||
`${new this.entityType()
|
||||
.getCrudApiPath()
|
||||
?.toString()}/vars`,
|
||||
`${new this.entityType().getCrudApiPath()?.toString()}/vars`,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
const globalConfig = await GlobalConfigService.findOneById({
|
||||
id: ObjectID.getZeroObjectID(),
|
||||
select: {
|
||||
host: true,
|
||||
useHttps: true
|
||||
},
|
||||
props: {
|
||||
isRoot: true
|
||||
}
|
||||
const globalConfig: GlobalConfig | null =
|
||||
await GlobalConfigService.findOneById({
|
||||
id: ObjectID.getZeroObjectID(),
|
||||
select: {
|
||||
useHttps: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, {
|
||||
USE_HTTPS:
|
||||
globalConfig?.useHttps?.toString() || 'false',
|
||||
});
|
||||
|
||||
return Response.sendJsonObjectResponse(
|
||||
req,
|
||||
res,
|
||||
{
|
||||
'HOST': globalConfig?.host?.toString() || 'localhost',
|
||||
'USE_HTTPS': globalConfig?.useHttps?.toString() || "false"
|
||||
},
|
||||
|
||||
);
|
||||
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import {
|
||||
} from '../Utils/Express';
|
||||
import TeamMemberService from '../Services/TeamMemberService';
|
||||
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import NotAuthorizedException from 'Common/Types/Exception/NotAuthorizedException';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import Response from '../Utils/Response';
|
||||
import TeamMember from 'Model/Models/TeamMember';
|
||||
import NotAuthenticatedException from 'Common/Types/Exception/NotAuthenticatedException';
|
||||
|
||||
export default class ProjectAPI extends BaseAPI<Project, ProjectServiceType> {
|
||||
public constructor() {
|
||||
@@ -35,7 +35,7 @@ export default class ProjectAPI extends BaseAPI<Project, ProjectServiceType> {
|
||||
) => {
|
||||
try {
|
||||
if (!(req as OneUptimeRequest).userAuthorization?.userId) {
|
||||
throw new NotAuthorizedException(
|
||||
throw new NotAuthenticatedException(
|
||||
'User should be logged in to access this API'
|
||||
);
|
||||
}
|
||||
@@ -45,6 +45,7 @@ export default class ProjectAPI extends BaseAPI<Project, ProjectServiceType> {
|
||||
query: {
|
||||
userId: (req as OneUptimeRequest)
|
||||
.userAuthorization!.userId!,
|
||||
hasAcceptedInvitation: true,
|
||||
},
|
||||
select: {
|
||||
project: {
|
||||
|
||||
@@ -205,6 +205,7 @@ export default class ResellerPlanAPI extends BaseAPI<
|
||||
data: {
|
||||
activeMonitorsLimit: resellerPlan.monitorLimit!,
|
||||
seatLimit: resellerPlan.teamMemberLimit!,
|
||||
resellerPlanId: resellerPlan.id!,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
|
||||
@@ -15,7 +15,7 @@ import Response from '../Utils/Response';
|
||||
import NotAuthenticatedException from 'Common/Types/Exception/NotAuthenticatedException';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import StatusPageFooterLinkService from '../Services/StatusPageFooterLinkService';
|
||||
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import LIMIT_MAX, { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import StatusPageFooterLink from 'Model/Models/StatusPageFooterLink';
|
||||
import StatusPageHeaderLinkService from '../Services/StatusPageHeaderLinkService';
|
||||
import StatusPageHeaderLink from 'Model/Models/StatusPageHeaderLink';
|
||||
@@ -553,7 +553,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
createdAt: SortOrder.Ascending,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
limit: LIMIT_MAX, // This can be optimized.
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
|
||||
@@ -31,9 +31,8 @@ export default class DatabaseConfig {
|
||||
}
|
||||
|
||||
public static async getHost(): Promise<Hostname> {
|
||||
return (
|
||||
((await DatabaseConfig.getFromGlobalConfig('host')) as Hostname) ||
|
||||
new Hostname('localhost')
|
||||
return Promise.resolve(
|
||||
new Hostname(process.env['HOST'] || 'localhost')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,49 +62,28 @@ export const ClusterKey: ObjectID = new ObjectID(
|
||||
|
||||
export const HasClusterKey: boolean = Boolean(process.env['ONEUPTIME_SECRET']);
|
||||
|
||||
export const RealtimeHostname: Hostname = Hostname.fromString(
|
||||
process.env['REALTIME_HOSTNAME'] || 'realtime'
|
||||
);
|
||||
export const RealtimeHostname: Hostname = Hostname.fromString('realtime:3300');
|
||||
|
||||
export const NotificationHostname: Hostname = Hostname.fromString(
|
||||
process.env['NOTIFICATION_HOSTNAME'] || 'notification'
|
||||
);
|
||||
export const NotificationHostname: Hostname =
|
||||
Hostname.fromString('notification:3191');
|
||||
|
||||
export const WorkerHostname: Hostname = Hostname.fromString(
|
||||
process.env['WORKER_HOSTNAME'] || 'worker'
|
||||
);
|
||||
export const WorkerHostname: Hostname = Hostname.fromString('worker:3452');
|
||||
|
||||
export const LinkShortenerHostname: Route = new Route(
|
||||
process.env['LINK_SHORTENER_HOSTNAME'] || 'link-shortener'
|
||||
);
|
||||
export const LinkShortenerHostname: Route = new Route('link-shortener:3521');
|
||||
|
||||
export const WorkflowHostname: Hostname = Hostname.fromString(
|
||||
process.env['WORKFLOW_HOSTNAME'] || 'workflow'
|
||||
);
|
||||
export const WorkflowHostname: Hostname = Hostname.fromString('workflow:3099');
|
||||
|
||||
export const DashboardApiHostname: Hostname = Hostname.fromString(
|
||||
process.env['DASHBOARD_API_HOSTNAME'] || 'dashboard-api'
|
||||
);
|
||||
export const DashboardApiHostname: Hostname =
|
||||
Hostname.fromString('dashboard-api:3002');
|
||||
|
||||
export const ProbeApiHostname: Hostname = Hostname.fromString(
|
||||
process.env['PROBE_API_HOSTNAME'] || 'probe-api'
|
||||
);
|
||||
export const ProbeApiHostname: Hostname = Hostname.fromString('probe-api:3400');
|
||||
|
||||
export const DataIngestorHostname: Hostname = Hostname.fromString(
|
||||
process.env['DATA_INGESTOR_HOSTNAME'] || 'daat-ingestor'
|
||||
);
|
||||
export const AccountsHostname: Hostname = Hostname.fromString('accounts:3003');
|
||||
|
||||
export const AccountsHostname: Hostname = Hostname.fromString(
|
||||
process.env['ACCOUNTS_HOSTNAME'] || 'accounts'
|
||||
);
|
||||
export const HomeHostname: Hostname = Hostname.fromString('home:1444');
|
||||
|
||||
export const HomeHostname: Hostname = Hostname.fromString(
|
||||
process.env['HOME_HOSTNAME'] || 'home'
|
||||
);
|
||||
|
||||
export const DashboardHostname: Hostname = Hostname.fromString(
|
||||
process.env['DASHBOARD_HOSTNAME'] || 'dashboard'
|
||||
);
|
||||
export const DashboardHostname: Hostname =
|
||||
Hostname.fromString('dashboard:3000');
|
||||
|
||||
export const Env: string = process.env['NODE_ENV'] || 'production';
|
||||
|
||||
|
||||
@@ -14,8 +14,12 @@ import UserType from 'Common/Types/UserType';
|
||||
import AccessTokenService from '../Services/AccessTokenService';
|
||||
import { UserTenantAccessPermission } from 'Common/Types/Permission';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import Response from '../Utils/Response';
|
||||
import QueryHelper from '../Types/Database/QueryHelper';
|
||||
import GlobalConfigService from '../Services/GlobalConfigService';
|
||||
import User from 'Model/Models/User';
|
||||
import UserService from '../Services/UserService';
|
||||
import GlobalConfig from 'Model/Models/GlobalConfig';
|
||||
import logger from '../Utils/Logger';
|
||||
|
||||
export default class ProjectMiddleware {
|
||||
public static getProjectId(req: ExpressRequest): ObjectID | null {
|
||||
@@ -55,67 +59,132 @@ export default class ProjectMiddleware {
|
||||
|
||||
public static async isValidProjectIdAndApiKeyMiddleware(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
_res: ExpressResponse,
|
||||
next: NextFunction
|
||||
): Promise<void> {
|
||||
const tenantId: ObjectID | null = this.getProjectId(req);
|
||||
try {
|
||||
const tenantId: ObjectID | null = this.getProjectId(req);
|
||||
|
||||
const apiKey: ObjectID | null = this.getApiKey(req);
|
||||
logger.info('tenantId', tenantId);
|
||||
|
||||
if (!tenantId) {
|
||||
throw new BadDataException('ProjectId not found in the request');
|
||||
}
|
||||
const apiKey: ObjectID | null = this.getApiKey(req);
|
||||
|
||||
if (!apiKey) {
|
||||
throw new BadDataException('ApiKey not found in the request');
|
||||
}
|
||||
|
||||
const apiKeyModel: ApiKey | null = await ApiKeyService.findOneBy({
|
||||
query: {
|
||||
projectId: tenantId,
|
||||
apiKey: apiKey,
|
||||
expiresAt: QueryHelper.greaterThan(
|
||||
OneUptimeDate.getCurrentDate()
|
||||
),
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
props: { isRoot: true },
|
||||
});
|
||||
|
||||
if (apiKeyModel) {
|
||||
(req as OneUptimeRequest).userType = UserType.API;
|
||||
// TODO: Add API key permissions.
|
||||
// (req as OneUptimeRequest).permissions =
|
||||
// apiKeyModel.permissions || [];
|
||||
(req as OneUptimeRequest).tenantId = tenantId;
|
||||
(req as OneUptimeRequest).userGlobalAccessPermission =
|
||||
await AccessTokenService.getDefaultApiGlobalPermission(
|
||||
tenantId
|
||||
);
|
||||
|
||||
const userTenantAccessPermission: UserTenantAccessPermission | null =
|
||||
await AccessTokenService.getApiTenantAccessPermission(
|
||||
tenantId,
|
||||
apiKeyModel.id!
|
||||
);
|
||||
|
||||
if (userTenantAccessPermission) {
|
||||
(req as OneUptimeRequest).userTenantAccessPermission = {};
|
||||
(
|
||||
(req as OneUptimeRequest)
|
||||
.userTenantAccessPermission as Dictionary<UserTenantAccessPermission>
|
||||
)[tenantId.toString()] = userTenantAccessPermission;
|
||||
|
||||
return next();
|
||||
if (tenantId) {
|
||||
(req as OneUptimeRequest).tenantId = tenantId;
|
||||
}
|
||||
}
|
||||
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid Project ID or API Key')
|
||||
);
|
||||
if (!apiKey) {
|
||||
throw new BadDataException('ApiKey not found in the request');
|
||||
}
|
||||
|
||||
let apiKeyModel: ApiKey | null = null;
|
||||
|
||||
if (tenantId) {
|
||||
apiKeyModel = await ApiKeyService.findOneBy({
|
||||
query: {
|
||||
projectId: tenantId,
|
||||
apiKey: apiKey,
|
||||
expiresAt: QueryHelper.greaterThan(
|
||||
OneUptimeDate.getCurrentDate()
|
||||
),
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
props: { isRoot: true },
|
||||
});
|
||||
|
||||
if (apiKeyModel) {
|
||||
(req as OneUptimeRequest).userType = UserType.API;
|
||||
// TODO: Add API key permissions.
|
||||
// (req as OneUptimeRequest).permissions =
|
||||
// apiKeyModel.permissions || [];
|
||||
(req as OneUptimeRequest).userGlobalAccessPermission =
|
||||
await AccessTokenService.getDefaultApiGlobalPermission(
|
||||
tenantId
|
||||
);
|
||||
|
||||
const userTenantAccessPermission: UserTenantAccessPermission | null =
|
||||
await AccessTokenService.getApiTenantAccessPermission(
|
||||
tenantId,
|
||||
apiKeyModel.id!
|
||||
);
|
||||
|
||||
if (userTenantAccessPermission) {
|
||||
(req as OneUptimeRequest).userTenantAccessPermission =
|
||||
{};
|
||||
(
|
||||
(req as OneUptimeRequest)
|
||||
.userTenantAccessPermission as Dictionary<UserTenantAccessPermission>
|
||||
)[tenantId.toString()] = userTenantAccessPermission;
|
||||
|
||||
return next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!apiKeyModel) {
|
||||
// check master key.
|
||||
const masterKeyGlobalConfig: GlobalConfig | null =
|
||||
await GlobalConfigService.findOneBy({
|
||||
query: {
|
||||
_id: ObjectID.getZeroObjectID().toString(),
|
||||
isMasterApiKeyEnabled: true,
|
||||
masterApiKey: apiKey,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (masterKeyGlobalConfig) {
|
||||
(req as OneUptimeRequest).userType = UserType.MasterAdmin;
|
||||
|
||||
// get master admin user
|
||||
|
||||
const user: User | null = await UserService.findOneBy({
|
||||
query: {
|
||||
isMasterAdmin: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new BadDataException(
|
||||
'Master Admin user not found. Please make sure you have created a master admin user.'
|
||||
);
|
||||
}
|
||||
|
||||
(req as OneUptimeRequest).userAuthorization = {
|
||||
userId: user.id!,
|
||||
isMasterAdmin: true,
|
||||
email: user.email!,
|
||||
name: user.name!,
|
||||
};
|
||||
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
if (!tenantId) {
|
||||
throw new BadDataException(
|
||||
'ProjectID not found in the request header.'
|
||||
);
|
||||
}
|
||||
|
||||
throw new BadDataException('Invalid Project ID or API Key');
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,40 @@ export class AccessTokenService extends BaseService {
|
||||
};
|
||||
}
|
||||
|
||||
public async getMasterKeyApiGlobalPermission(
|
||||
projectId: ObjectID
|
||||
): Promise<UserGlobalAccessPermission> {
|
||||
return {
|
||||
projectIds: [projectId],
|
||||
globalPermissions: [
|
||||
Permission.Public,
|
||||
Permission.User,
|
||||
Permission.CurrentUser,
|
||||
Permission.ProjectOwner,
|
||||
],
|
||||
_type: 'UserGlobalAccessPermission',
|
||||
};
|
||||
}
|
||||
|
||||
public async getMasterApiTenantAccessPermission(
|
||||
projectId: ObjectID
|
||||
): Promise<UserTenantAccessPermission> {
|
||||
const userPermissions: Array<UserPermission> = [];
|
||||
|
||||
userPermissions.push({
|
||||
permission: Permission.ProjectOwner,
|
||||
labelIds: [],
|
||||
_type: 'UserPermission',
|
||||
});
|
||||
|
||||
const permission: UserTenantAccessPermission =
|
||||
this.getDefaultUserTenantAccessPermission(projectId);
|
||||
|
||||
permission.permissions = permission.permissions.concat(userPermissions);
|
||||
|
||||
return permission;
|
||||
}
|
||||
|
||||
public async getApiTenantAccessPermission(
|
||||
projectId: ObjectID,
|
||||
apiKeyId: ObjectID
|
||||
|
||||
@@ -965,6 +965,7 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
||||
const onDelete: OnDelete<TBaseModel> = deleteBy.props.ignoreHooks
|
||||
? { deleteBy, carryForward: [] }
|
||||
: await this.onBeforeDelete(deleteBy);
|
||||
|
||||
const beforeDeleteBy: DeleteBy<TBaseModel> = onDelete.deleteBy;
|
||||
|
||||
const carryForward: any = onDelete.carryForward;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/Incident';
|
||||
import DatabaseService, { OnCreate, OnUpdate } from './DatabaseService';
|
||||
import DatabaseService, {
|
||||
OnCreate,
|
||||
OnDelete,
|
||||
OnUpdate,
|
||||
} from './DatabaseService';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Monitor from 'Model/Models/Monitor';
|
||||
import MonitorService from './MonitorService';
|
||||
@@ -19,13 +23,20 @@ import Typeof from 'Common/Types/Typeof';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import User from 'Model/Models/User';
|
||||
import TeamMemberService from './TeamMemberService';
|
||||
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import LIMIT_MAX, { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import UserService from './UserService';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import OnCallDutyPolicyService from './OnCallDutyPolicyService';
|
||||
import UserNotificationEventType from 'Common/Types/UserNotification/UserNotificationEventType';
|
||||
import SortOrder from 'Common/Types/Database/SortOrder';
|
||||
import DatabaseConfig from '../DatabaseConfig';
|
||||
import MonitorStatus from 'Model/Models/MonitorStatus';
|
||||
import MonitorStatusService from './MonitorStatusService';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import QueryHelper from '../Types/Database/QueryHelper';
|
||||
import MonitorStatusTimeline from 'Model/Models/MonitorStatusTimeline';
|
||||
import MonitorStatusTimelineService from './MonitorStatusTimelineService';
|
||||
import DeleteBy from '../Types/Database/DeleteBy';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
@@ -411,6 +422,177 @@ export class Service extends DatabaseService<Model> {
|
||||
return onUpdate;
|
||||
}
|
||||
|
||||
public async doesMonitorHasMoreActiveManualIncidents(
|
||||
monitorId: ObjectID,
|
||||
proojectId: ObjectID
|
||||
): Promise<boolean> {
|
||||
const resolvedState: IncidentState | null =
|
||||
await IncidentStateService.findOneBy({
|
||||
query: {
|
||||
projectId: proojectId,
|
||||
isResolvedState: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
order: true,
|
||||
},
|
||||
});
|
||||
|
||||
const incidentCount: PositiveNumber = await this.countBy({
|
||||
query: {
|
||||
monitors: QueryHelper.inRelationArray([monitorId]),
|
||||
currentIncidentState: {
|
||||
order: QueryHelper.lessThan(resolvedState?.order!),
|
||||
},
|
||||
isCreatedAutomatically: false,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return incidentCount.toNumber() > 0;
|
||||
}
|
||||
|
||||
public async markMonitorsActiveForMonitoring(
|
||||
projectId: ObjectID,
|
||||
monitors: Array<Monitor>
|
||||
): Promise<void> {
|
||||
// resolve all the monitors.
|
||||
|
||||
if (monitors.length > 0) {
|
||||
// get resolved monitor state.
|
||||
const resolvedMonitorState: MonitorStatus | null =
|
||||
await MonitorStatusService.findOneBy({
|
||||
query: {
|
||||
projectId: projectId!,
|
||||
isOperationalState: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (resolvedMonitorState) {
|
||||
for (const monitor of monitors) {
|
||||
//check state of the monitor.
|
||||
|
||||
const doesMonitorHasMoreActiveManualIncidents: boolean =
|
||||
await this.doesMonitorHasMoreActiveManualIncidents(
|
||||
monitor.id!,
|
||||
projectId!
|
||||
);
|
||||
|
||||
if (doesMonitorHasMoreActiveManualIncidents) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await MonitorService.updateOneById({
|
||||
id: monitor.id!,
|
||||
data: {
|
||||
disableActiveMonitoringBecauseOfManualIncident:
|
||||
false,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const latestState: MonitorStatusTimeline | null =
|
||||
await MonitorStatusTimelineService.findOneBy({
|
||||
query: {
|
||||
monitorId: monitor.id!,
|
||||
projectId: projectId!,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
monitorStatusId: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
sort: {
|
||||
createdAt: SortOrder.Descending,
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
latestState &&
|
||||
latestState.monitorStatusId?.toString() ===
|
||||
resolvedMonitorState.id!.toString()
|
||||
) {
|
||||
// already on this state. Skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
const monitorStatusTimeline: MonitorStatusTimeline =
|
||||
new MonitorStatusTimeline();
|
||||
monitorStatusTimeline.monitorId = monitor.id!;
|
||||
monitorStatusTimeline.projectId = projectId!;
|
||||
monitorStatusTimeline.monitorStatusId =
|
||||
resolvedMonitorState.id!;
|
||||
|
||||
await MonitorStatusTimelineService.create({
|
||||
data: monitorStatusTimeline,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async onBeforeDelete(
|
||||
deleteBy: DeleteBy<Model>
|
||||
): Promise<OnDelete<Model>> {
|
||||
const incidents: Array<Model> = await this.findBy({
|
||||
query: deleteBy.query,
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
select: {
|
||||
projectId: true,
|
||||
monitors: {
|
||||
_id: true,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
deleteBy,
|
||||
carryForward: {
|
||||
incidents: incidents,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override async onDeleteSuccess(
|
||||
onDelete: OnDelete<Model>,
|
||||
_itemIdsBeforeDelete: ObjectID[]
|
||||
): Promise<OnDelete<Model>> {
|
||||
if (onDelete.carryForward && onDelete.carryForward.incidents) {
|
||||
for (const incident of onDelete.carryForward.incidents) {
|
||||
if (incident.monitors && incident.monitors.length > 0) {
|
||||
await this.markMonitorsActiveForMonitoring(
|
||||
incident.projectId!,
|
||||
incident.monitors
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return onDelete;
|
||||
}
|
||||
|
||||
public async changeIncidentState(
|
||||
projectId: ObjectID,
|
||||
incidentId: ObjectID,
|
||||
|
||||
@@ -9,16 +9,11 @@ import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import SortOrder from 'Common/Types/Database/SortOrder';
|
||||
import IncidentState from 'Model/Models/IncidentState';
|
||||
import IncidentStateService from './IncidentStateService';
|
||||
import Incident from 'Model/Models/Incident';
|
||||
import MonitorStatusService from './MonitorStatusService';
|
||||
import MonitorStatus from 'Model/Models/MonitorStatus';
|
||||
import MonitorStatusTimeline from 'Model/Models/MonitorStatusTimeline';
|
||||
import MonitorStatusTimelineService from './MonitorStatusTimelineService';
|
||||
import CreateBy from '../Types/Database/CreateBy';
|
||||
import UserService from './UserService';
|
||||
import User from 'Model/Models/User';
|
||||
import MonitorService from './MonitorService';
|
||||
import QueryHelper from '../Types/Database/QueryHelper';
|
||||
import Incident from 'Model/Models/Incident';
|
||||
|
||||
export class Service extends DatabaseService<IncidentStateTimeline> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
@@ -132,10 +127,9 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
|
||||
});
|
||||
|
||||
if (isResolvedState) {
|
||||
// resolve all the monitors.
|
||||
const incident: Incident | null = await IncidentService.findOneBy({
|
||||
query: {
|
||||
_id: createdItem.incidentId?.toString(),
|
||||
_id: createdItem.incidentId.toString(),
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
@@ -149,130 +143,17 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
|
||||
},
|
||||
});
|
||||
|
||||
if (incident && incident.monitors && incident.monitors.length > 0) {
|
||||
// get resolved monitor state.
|
||||
const resolvedMonitorState: MonitorStatus | null =
|
||||
await MonitorStatusService.findOneBy({
|
||||
query: {
|
||||
projectId: incident.projectId!,
|
||||
isOperationalState: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (resolvedMonitorState) {
|
||||
for (const monitor of incident.monitors) {
|
||||
//check state of the monitor.
|
||||
|
||||
const doesMonitorHasMoreActiveManualIncidents: boolean =
|
||||
await this.doesMonitorHasMoreActiveManualIncidents(
|
||||
monitor.id!,
|
||||
incident.projectId!
|
||||
);
|
||||
|
||||
if (doesMonitorHasMoreActiveManualIncidents) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await MonitorService.updateOneById({
|
||||
id: monitor.id!,
|
||||
data: {
|
||||
disableActiveMonitoringBecauseOfManualIncident:
|
||||
false,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
const latestState: MonitorStatusTimeline | null =
|
||||
await MonitorStatusTimelineService.findOneBy({
|
||||
query: {
|
||||
monitorId: monitor.id!,
|
||||
projectId: incident.projectId!,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
monitorStatusId: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
sort: {
|
||||
createdAt: SortOrder.Descending,
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
latestState &&
|
||||
latestState.monitorStatusId?.toString() ===
|
||||
resolvedMonitorState.id!.toString()
|
||||
) {
|
||||
// already on this state. Skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
const monitorStatusTimeline: MonitorStatusTimeline =
|
||||
new MonitorStatusTimeline();
|
||||
monitorStatusTimeline.monitorId = monitor.id!;
|
||||
monitorStatusTimeline.projectId = incident.projectId!;
|
||||
monitorStatusTimeline.monitorStatusId =
|
||||
resolvedMonitorState.id!;
|
||||
|
||||
await MonitorStatusTimelineService.create({
|
||||
data: monitorStatusTimeline,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
if (incident) {
|
||||
await IncidentService.markMonitorsActiveForMonitoring(
|
||||
incident.projectId!,
|
||||
incident.monitors || []
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return createdItem;
|
||||
}
|
||||
|
||||
public async doesMonitorHasMoreActiveManualIncidents(
|
||||
monitorId: ObjectID,
|
||||
proojectId: ObjectID
|
||||
): Promise<boolean> {
|
||||
const resolvedState: IncidentState | null =
|
||||
await IncidentStateService.findOneBy({
|
||||
query: {
|
||||
projectId: proojectId,
|
||||
isResolvedState: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
order: true,
|
||||
},
|
||||
});
|
||||
|
||||
const incidentCount: PositiveNumber = await IncidentService.countBy({
|
||||
query: {
|
||||
monitors: QueryHelper.inRelationArray([monitorId]),
|
||||
currentIncidentState: {
|
||||
order: QueryHelper.lessThan(resolvedState?.order!),
|
||||
},
|
||||
isCreatedAutomatically: false,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return incidentCount.toNumber() > 0;
|
||||
}
|
||||
|
||||
protected override async onBeforeDelete(
|
||||
deleteBy: DeleteBy<IncidentStateTimeline>
|
||||
): Promise<OnDelete<IncidentStateTimeline>> {
|
||||
|
||||
@@ -14,12 +14,44 @@ import { FileRoute } from 'Common/ServiceRoute';
|
||||
import DatabaseConfig from '../DatabaseConfig';
|
||||
import Hostname from 'Common/Types/API/Hostname';
|
||||
import Protocol from 'Common/Types/API/Protocol';
|
||||
import CreateBy from '../Types/Database/CreateBy';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
|
||||
protected override async onBeforeCreate(
|
||||
createBy: CreateBy<Model>
|
||||
): Promise<OnCreate<Model>> {
|
||||
// check if this user is already invited.
|
||||
if (createBy.data.statusPageId && createBy.data.email) {
|
||||
const statusPageUser: Model | null = await this.findOneBy({
|
||||
query: {
|
||||
email: createBy.data.email,
|
||||
statusPageId: createBy.data.statusPageId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (statusPageUser) {
|
||||
throw new BadDataException(
|
||||
'This user is already invited to this status page.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
createBy: createBy,
|
||||
carryForward: null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override async onCreateSuccess(
|
||||
_onCreate: OnCreate<Model>,
|
||||
createdItem: Model
|
||||
|
||||
@@ -6,7 +6,7 @@ import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import StatusPageService from './StatusPageService';
|
||||
import MailService from './MailService';
|
||||
import EmailTemplateType from 'Common/Types/Email/EmailTemplateType';
|
||||
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { FileRoute } from 'Common/ServiceRoute';
|
||||
import DatabaseConfig from '../DatabaseConfig';
|
||||
@@ -175,7 +175,7 @@ export class Service extends DatabaseService<Model> {
|
||||
subscriberWebhook: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
limit: LIMIT_MAX,
|
||||
props: props,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import QueryHelper from '../Types/Database/QueryHelper';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import ProjectService from './ProjectService';
|
||||
import { IsBillingEnabled } from '../EnvironmentConfig';
|
||||
import { DashboardRoute } from 'Common/ServiceRoute';
|
||||
import { AccountsRoute } from 'Common/ServiceRoute';
|
||||
import DatabaseConfig from '../DatabaseConfig';
|
||||
import BillingService from './BillingService';
|
||||
import SubscriptionPlan from 'Common/Types/Billing/SubscriptionPlan';
|
||||
@@ -78,7 +78,10 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
|
||||
isRoot: true,
|
||||
});
|
||||
|
||||
let isNewUser: boolean = false;
|
||||
|
||||
if (!user) {
|
||||
isNewUser = true;
|
||||
user = await UserService.createByEmail(email, {
|
||||
isRoot: true,
|
||||
});
|
||||
@@ -106,11 +109,24 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
|
||||
toEmail: email,
|
||||
templateType: EmailTemplateType.InviteMember,
|
||||
vars: {
|
||||
dashboardUrl: new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
DashboardRoute
|
||||
signInLink: URL.fromString(
|
||||
new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
AccountsRoute
|
||||
).toString()
|
||||
).toString(),
|
||||
registerLink: URL.fromString(
|
||||
new URL(
|
||||
httpProtocol,
|
||||
host,
|
||||
AccountsRoute
|
||||
).toString()
|
||||
)
|
||||
.addRoute('/register')
|
||||
.addQueryParam('email', email.toString())
|
||||
.toString(),
|
||||
isNewUser: isNewUser.toString(),
|
||||
projectName: project.name!,
|
||||
homeUrl: new URL(httpProtocol, host).toString(),
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import '../TestingUtils/Init';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import ProjectMiddleware from '../../Middleware/ProjectAuthorization';
|
||||
import {
|
||||
@@ -6,13 +7,14 @@ import {
|
||||
NextFunction,
|
||||
} from '../../Utils/Express';
|
||||
import ApiKeyService from '../../Services/ApiKeyService';
|
||||
import Response from '../../Utils/Response';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import QueryHelper from '../../Types/Database/QueryHelper';
|
||||
import ApiKey from 'Model/Models/ApiKey';
|
||||
import AccessTokenService from '../../Services/AccessTokenService';
|
||||
import { UserTenantAccessPermission } from 'Common/Types/Permission';
|
||||
import Database from '../TestingUtils/Database';
|
||||
import GlobalConfigService from '../../Services/GlobalConfigService';
|
||||
|
||||
jest.mock('../../Services/ApiKeyService');
|
||||
jest.mock('../../Services/AccessTokenService');
|
||||
@@ -97,110 +99,123 @@ describe('ProjectMiddleware', () => {
|
||||
const req: ExpressRequest = { headers: {} } as ExpressRequest;
|
||||
|
||||
test('should return true when getApiKey returns a non-null value', () => {
|
||||
const spyGetApiKey: jest.SpyInstance = jest
|
||||
.spyOn(ProjectMiddleware, 'getApiKey')
|
||||
.mockReturnValue(mockedObjectId);
|
||||
req.headers['apikey'] = mockedObjectId.toString();
|
||||
|
||||
const result: boolean = ProjectMiddleware.hasApiKey(req);
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
expect(spyGetApiKey).toHaveBeenCalledWith(req);
|
||||
});
|
||||
|
||||
test('should return false when getApiKey returns null', () => {
|
||||
const spyGetApiKey: jest.SpyInstance = jest
|
||||
.spyOn(ProjectMiddleware, 'getApiKey')
|
||||
.mockReturnValue(null);
|
||||
req.headers['apikey'] = undefined;
|
||||
|
||||
const result: boolean = ProjectMiddleware.hasApiKey(req);
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
expect(spyGetApiKey).toHaveBeenCalledWith(req);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasProjectID', () => {
|
||||
const req: ExpressRequest = { headers: {} } as ExpressRequest;
|
||||
test('should return true when getProjectId returns a non-null value', () => {
|
||||
const spyGetProjectId: jest.SpyInstance = jest
|
||||
.spyOn(ProjectMiddleware, 'getProjectId')
|
||||
.mockReturnValue(mockedObjectId);
|
||||
req.headers['tenantid'] = mockedObjectId.toString();
|
||||
|
||||
const result: boolean = ProjectMiddleware.hasProjectID(req);
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
expect(spyGetProjectId).toHaveBeenCalledWith(req);
|
||||
});
|
||||
|
||||
test('should return false when getProjectId returns null', () => {
|
||||
const spyGetProjectId: jest.SpyInstance = jest
|
||||
.spyOn(ProjectMiddleware, 'getProjectId')
|
||||
.mockReturnValue(null);
|
||||
req.headers['tenantid'] = undefined;
|
||||
|
||||
const result: boolean = ProjectMiddleware.hasProjectID(req);
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
expect(spyGetProjectId).toHaveBeenCalledWith(req);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValidProjectIdAndApiKeyMiddleware', () => {
|
||||
const req: ExpressRequest = {} as ExpressRequest;
|
||||
const res: ExpressResponse = {} as ExpressResponse;
|
||||
const next: NextFunction = jest.fn();
|
||||
let next: NextFunction = jest.fn();
|
||||
|
||||
const mockedApiModel: ApiKey = {
|
||||
id: mockedObjectId,
|
||||
} as ApiKey;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.spyOn(ProjectMiddleware, 'getProjectId').mockReturnValue(
|
||||
mockedObjectId
|
||||
);
|
||||
jest.spyOn(ProjectMiddleware, 'getApiKey').mockReturnValue(
|
||||
mockedObjectId
|
||||
);
|
||||
let database!: Database;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
next = jest.fn();
|
||||
database = new Database();
|
||||
await database.createAndConnect();
|
||||
|
||||
if (req.headers === undefined) {
|
||||
req.headers = {};
|
||||
}
|
||||
|
||||
req.headers['tenantid'] = mockedObjectId.toString();
|
||||
req.headers['apikey'] = mockedObjectId.toString();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await database.disconnectAndDropDatabase();
|
||||
});
|
||||
|
||||
test('should throw BadDataException when getProjectId returns null', async () => {
|
||||
const spyGetProjectId: jest.SpyInstance = jest
|
||||
.spyOn(ProjectMiddleware, 'getProjectId')
|
||||
.mockReturnValueOnce(null);
|
||||
const spyFindOneBy: jest.SpyInstance = jest
|
||||
.spyOn(GlobalConfigService, 'findOneBy')
|
||||
.mockResolvedValue(null);
|
||||
|
||||
await expect(
|
||||
ProjectMiddleware.isValidProjectIdAndApiKeyMiddleware(
|
||||
req,
|
||||
res,
|
||||
next
|
||||
req.headers['tenantid'] = undefined;
|
||||
req.headers['apikey'] = mockedObjectId.toString();
|
||||
|
||||
await ProjectMiddleware.isValidProjectIdAndApiKeyMiddleware(
|
||||
req,
|
||||
res,
|
||||
next
|
||||
);
|
||||
|
||||
expect(spyFindOneBy).toHaveBeenCalledWith({
|
||||
query: {
|
||||
_id: ObjectID.getZeroObjectID().toString(),
|
||||
isMasterApiKeyEnabled: true,
|
||||
masterApiKey: mockedObjectId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
new BadDataException(
|
||||
'ProjectID not found in the request header.'
|
||||
)
|
||||
).rejects.toThrowError('ProjectId not found in the request');
|
||||
|
||||
expect(spyGetProjectId).toHaveBeenCalledWith(req);
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw BadDataException when getApiKey returns null', async () => {
|
||||
const spyGetApiKey: jest.SpyInstance = jest
|
||||
.spyOn(ProjectMiddleware, 'getApiKey')
|
||||
.mockReturnValueOnce(null);
|
||||
req.headers['apikey'] = undefined;
|
||||
|
||||
await expect(
|
||||
ProjectMiddleware.isValidProjectIdAndApiKeyMiddleware(
|
||||
req,
|
||||
res,
|
||||
next
|
||||
)
|
||||
).rejects.toThrowError('ApiKey not found in the request');
|
||||
await ProjectMiddleware.isValidProjectIdAndApiKeyMiddleware(
|
||||
req,
|
||||
res,
|
||||
next
|
||||
);
|
||||
|
||||
expect(spyGetApiKey).toHaveBeenCalledWith(req);
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
new BadDataException('ApiKey not found in the request')
|
||||
);
|
||||
});
|
||||
|
||||
test('should call Response.sendErrorResponse when apiKeyModel is null', async () => {
|
||||
const spyFindOneBy: jest.SpyInstance = jest
|
||||
.spyOn(ApiKeyService, 'findOneBy')
|
||||
.mockResolvedValue(null);
|
||||
const spySendErrorResponse: jest.SpyInstance = jest
|
||||
.spyOn(Response, 'sendErrorResponse')
|
||||
.mockImplementation(jest.fn);
|
||||
|
||||
jest.spyOn(QueryHelper, 'greaterThan').mockImplementation(
|
||||
jest.fn()
|
||||
@@ -226,18 +241,12 @@ describe('ProjectMiddleware', () => {
|
||||
props: { isRoot: true },
|
||||
});
|
||||
|
||||
expect(spySendErrorResponse).toHaveBeenCalledWith(
|
||||
req,
|
||||
res,
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
new BadDataException('Invalid Project ID or API Key')
|
||||
);
|
||||
});
|
||||
|
||||
test('should call Response.sendErrorResponse when apiKeyModel is not null but getApiTenantAccessPermission returned null', async () => {
|
||||
const spySendErrorResponse: jest.SpyInstance = jest
|
||||
.spyOn(Response, 'sendErrorResponse')
|
||||
.mockImplementation(jest.fn);
|
||||
|
||||
jest.spyOn(ApiKeyService, 'findOneBy').mockResolvedValue(
|
||||
mockedApiModel
|
||||
);
|
||||
@@ -252,9 +261,8 @@ describe('ProjectMiddleware', () => {
|
||||
);
|
||||
|
||||
expect(spyGetApiTenantAccessPermission).toHaveBeenCalled();
|
||||
expect(spySendErrorResponse).toHaveBeenCalledWith(
|
||||
req,
|
||||
res,
|
||||
// check first param of next
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
new BadDataException('Invalid Project ID or API Key')
|
||||
);
|
||||
});
|
||||
|
||||
@@ -54,6 +54,14 @@ export default class ModelPermission {
|
||||
query: Query<TBaseModel>,
|
||||
props: DatabaseCommonInteractionProps
|
||||
): Promise<Query<TBaseModel>> {
|
||||
if (props.isRoot) {
|
||||
query = await this.addTenantScopeToQueryAsRoot(
|
||||
modelType,
|
||||
query,
|
||||
props
|
||||
);
|
||||
}
|
||||
|
||||
if (!props.isRoot) {
|
||||
this.checkModelLevelPermissions(
|
||||
modelType,
|
||||
@@ -265,6 +273,14 @@ export default class ModelPermission {
|
||||
): Promise<CheckReadPermissionType<TBaseModel>> {
|
||||
const model: BaseModel = new modelType();
|
||||
|
||||
if (props.isRoot) {
|
||||
query = await this.addTenantScopeToQueryAsRoot(
|
||||
modelType,
|
||||
query,
|
||||
props
|
||||
);
|
||||
}
|
||||
|
||||
if (!props.isRoot) {
|
||||
//check if the user is logged in.
|
||||
this.checkIfUserIsLoggedIn(
|
||||
@@ -804,6 +820,25 @@ export default class ModelPermission {
|
||||
}
|
||||
}
|
||||
|
||||
private static async addTenantScopeToQueryAsRoot<
|
||||
TBaseModel extends BaseModel
|
||||
>(
|
||||
modelType: { new (): TBaseModel },
|
||||
query: Query<TBaseModel>,
|
||||
props: DatabaseCommonInteractionProps
|
||||
): Promise<Query<TBaseModel>> {
|
||||
const model: BaseModel = new modelType();
|
||||
|
||||
const tenantColumn: string | null = model.getTenantColumn();
|
||||
|
||||
// If this model has a tenantColumn, and request has tenantId, and is multiTenantQuery null then add tenantId to query.
|
||||
if (tenantColumn && props.tenantId && !props.isMultiTenantRequest) {
|
||||
(query as any)[tenantColumn] = props.tenantId;
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
private static async addTenantScopeToQuery<TBaseModel extends BaseModel>(
|
||||
modelType: { new (): TBaseModel },
|
||||
query: Query<TBaseModel>,
|
||||
|
||||
@@ -17,6 +17,7 @@ import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import FileModel from 'Common/Models/FileModel';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import StatusCode from 'Common/Types/API/StatusCode';
|
||||
import { DEFAULT_LIMIT } from 'Common/Types/Database/LimitMax';
|
||||
|
||||
export default class Response {
|
||||
private static logResponse(
|
||||
@@ -276,6 +277,8 @@ export default class Response {
|
||||
listData.limit = new PositiveNumber(
|
||||
parseInt(oneUptimeRequest.query['limit'].toString())
|
||||
);
|
||||
} else {
|
||||
listData.limit = new PositiveNumber(DEFAULT_LIMIT);
|
||||
}
|
||||
|
||||
if (oneUptimeRequest.query['output-type'] === 'csv') {
|
||||
|
||||
@@ -28,7 +28,7 @@ import Response from './Response';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import API from 'Common/Utils/API';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { DashboardApiHostname } from '../EnvironmentConfig';
|
||||
import { AppVersion, DashboardApiHostname } from '../EnvironmentConfig';
|
||||
import { DashboardApiRoute } from 'Common/ServiceRoute';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
|
||||
@@ -95,8 +95,8 @@ app.use(setDefaultHeaders);
|
||||
* https://stackoverflow.com/questions/19917401/error-request-entity-too-large
|
||||
*/
|
||||
|
||||
app.use(ExpressJson({ limit: '50mb' }));
|
||||
app.use(ExpressUrlEncoded({ limit: '50mb' }));
|
||||
app.use(ExpressJson({ limit: '50mb', extended: true }));
|
||||
app.use(ExpressUrlEncoded({ limit: '50mb', extended: true }));
|
||||
|
||||
app.use(logRequest);
|
||||
|
||||
@@ -105,6 +105,8 @@ const init: Function = async (
|
||||
port?: Port,
|
||||
isFrontendApp?: boolean
|
||||
): Promise<ExpressApplication> => {
|
||||
logger.info(`App Version: ${AppVersion.toString()}`);
|
||||
|
||||
await Express.launchApplication(appName, port);
|
||||
LocalCache.setString('app', 'name', appName);
|
||||
CommonAPI(appName);
|
||||
|
||||
4
CommonServer/package-lock.json
generated
4
CommonServer/package-lock.json
generated
@@ -73,7 +73,7 @@
|
||||
"moment-timezone": "^0.5.40",
|
||||
"nanoid": "^3.3.2",
|
||||
"nanoid-dictionary": "^4.3.0",
|
||||
"posthog-js": "^1.37.0",
|
||||
"posthog-js": "^1.77.0",
|
||||
"process": "^0.11.10",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"slugify": "^1.6.5",
|
||||
@@ -81,7 +81,7 @@
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^6.3.1",
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^17.0.22",
|
||||
"jest": "^27.5.1",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"compile": "tsc",
|
||||
"test": "jest --detectOpenHandles",
|
||||
"debug:test": "node --inspect node_modules/.bin/jest --runInBand ./Tests --detectOpenHandles"
|
||||
"debug:test": "cd .. && export $(grep -v '^#' config.env | xargs) && cd CommonServer && node --inspect node_modules/.bin/jest --runInBand ./Tests --detectOpenHandles"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -18,6 +18,8 @@ import Field, { FormFieldStyleType } from '../Types/Field';
|
||||
import FieldLabelElement from '../Fields/FieldLabel';
|
||||
import FormValues from '../Types/FormValues';
|
||||
import { JSONValue } from 'Common/Types/JSON';
|
||||
import IDGenerator from '../../ObjectID/IDGenerator';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
|
||||
export interface ComponentProps<T extends Object> {
|
||||
field: Field<T>;
|
||||
@@ -167,6 +169,35 @@ const FormField: <T extends Object>(
|
||||
/>
|
||||
)}
|
||||
|
||||
{props.field.fieldType === FormFieldSchemaType.ObjectID && (
|
||||
<IDGenerator
|
||||
tabIndex={index}
|
||||
disabled={props.isDisabled || props.field.disabled}
|
||||
error={
|
||||
props.touched && props.error
|
||||
? props.error
|
||||
: undefined
|
||||
}
|
||||
dataTestId={fieldType}
|
||||
onChange={(value: ObjectID) => {
|
||||
props.field.onChange &&
|
||||
props.field.onChange(value);
|
||||
props.setFieldValue(props.fieldName, value);
|
||||
}}
|
||||
onEnterPress={() => {
|
||||
props.submitForm && props.submitForm();
|
||||
}}
|
||||
initialValue={
|
||||
props.currentValues &&
|
||||
(props.currentValues as any)[props.fieldName]
|
||||
? (props.currentValues as any)[
|
||||
props.fieldName
|
||||
]
|
||||
: props.field.defaultValue || null
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{props.field.fieldType ===
|
||||
FormFieldSchemaType.RadioButton && (
|
||||
<RadioButtons
|
||||
|
||||
@@ -118,7 +118,7 @@ const ModelForm: <TBaseModel extends BaseModel>(
|
||||
? (Object.keys(field.field)[0] as string)
|
||||
: null;
|
||||
|
||||
if (key && hasPermissionOnField(key)) {
|
||||
if (key && (hasPermissionOnField(key) || field.forceShow)) {
|
||||
(select as Dictionary<boolean>)[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1197,30 +1197,47 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
): ReactElement => {
|
||||
const plan: PlanSelect | null = ProjectUtil.getCurrentPlan();
|
||||
|
||||
let showPlan: boolean = Boolean(
|
||||
BILLING_ENABLED &&
|
||||
plan &&
|
||||
new props.modelType().readBillingPlan &&
|
||||
!SubscriptionPlan.isFeatureAccessibleOnCurrentPlan(
|
||||
new props.modelType().readBillingPlan!,
|
||||
plan,
|
||||
getAllEnvVars()
|
||||
)
|
||||
);
|
||||
|
||||
let planName: string = new props.modelType().readBillingPlan!;
|
||||
|
||||
if (props.isCreateable && !showPlan) {
|
||||
// if createable then read create billing permissions.
|
||||
showPlan = Boolean(
|
||||
BILLING_ENABLED &&
|
||||
plan &&
|
||||
new props.modelType().createBillingPlan &&
|
||||
!SubscriptionPlan.isFeatureAccessibleOnCurrentPlan(
|
||||
new props.modelType().createBillingPlan!,
|
||||
plan,
|
||||
getAllEnvVars()
|
||||
)
|
||||
);
|
||||
|
||||
planName = new props.modelType().createBillingPlan!;
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
{title}
|
||||
{BILLING_ENABLED &&
|
||||
plan &&
|
||||
new props.modelType().readBillingPlan &&
|
||||
!SubscriptionPlan.isFeatureAccessibleOnCurrentPlan(
|
||||
new props.modelType().readBillingPlan!,
|
||||
plan,
|
||||
getAllEnvVars()
|
||||
) && (
|
||||
<span
|
||||
style={{
|
||||
marginLeft: '5px',
|
||||
}}
|
||||
>
|
||||
<Pill
|
||||
text={`${
|
||||
new props.modelType().readBillingPlan
|
||||
} Plan`}
|
||||
color={Yellow}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
{showPlan && (
|
||||
<span
|
||||
style={{
|
||||
marginLeft: '5px',
|
||||
}}
|
||||
>
|
||||
<Pill text={`${planName} Plan`} color={Yellow} />
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
89
CommonUI/src/Components/ObjectID/IDGenerator.tsx
Normal file
89
CommonUI/src/Components/ObjectID/IDGenerator.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import Input from '../Input/Input';
|
||||
import Button, { ButtonStyleType } from '../Button/Button';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
|
||||
export interface ComponentProps {
|
||||
readonly?: boolean | undefined;
|
||||
initialValue?: undefined | ObjectID;
|
||||
onChange?: undefined | ((value: ObjectID) => void);
|
||||
value?: ObjectID | undefined;
|
||||
disabled?: boolean | undefined;
|
||||
dataTestId?: string | undefined;
|
||||
tabIndex?: number | undefined;
|
||||
onEnterPress?: (() => void) | undefined;
|
||||
error?: string | undefined;
|
||||
}
|
||||
|
||||
const IDGenerator: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const [value, setValue] = useState<ObjectID | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.initialValue) {
|
||||
setValue(props.initialValue);
|
||||
}
|
||||
|
||||
if (props.value) {
|
||||
setValue(props.value);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.initialValue) {
|
||||
setValue(props.initialValue);
|
||||
}
|
||||
}, [props.initialValue]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(props.value ? props.value : props.initialValue || null);
|
||||
}, [props.value]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<>
|
||||
<div className="flex" data-testid={props.dataTestId}>
|
||||
{value && (
|
||||
<Input
|
||||
readOnly={props.readonly}
|
||||
tabIndex={props.tabIndex}
|
||||
onEnterPress={props.onEnterPress}
|
||||
value={value.toString()}
|
||||
/>
|
||||
)}
|
||||
<div className="mt-2">
|
||||
<Button
|
||||
icon={IconProp.Refresh}
|
||||
buttonStyle={ButtonStyleType.NORMAL}
|
||||
disabled={props.disabled || props.readonly}
|
||||
title={value ? 'Regenerate' : 'Generate'}
|
||||
onClick={() => {
|
||||
const generatedID: ObjectID =
|
||||
ObjectID.generate();
|
||||
setValue(generatedID);
|
||||
props.onChange && props.onChange(generatedID);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
{props.error && (
|
||||
<p
|
||||
data-testid="error-message"
|
||||
className="mt-1 text-sm text-red-400"
|
||||
>
|
||||
{props.error}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default IDGenerator;
|
||||
@@ -22,11 +22,13 @@ import SubscriptionPlan from 'Common/Types/Billing/SubscriptionPlan';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
|
||||
export const getAllEnvVars: Function = (): JSONObject => {
|
||||
return window?.process?.env || process?.env || {};
|
||||
const envVars: JSONObject = window?.process?.env || process?.env || {};
|
||||
return envVars;
|
||||
};
|
||||
|
||||
export const env: Function = (key: string): string => {
|
||||
return (getAllEnvVars()[key] as string) || '';
|
||||
const allEnv: JSONObject = getAllEnvVars();
|
||||
return (allEnv[key] as string) || '';
|
||||
};
|
||||
|
||||
export const HTTP_PROTOCOL: Protocol =
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { API_DOCS_HOSTNAME, HTTP_PROTOCOL, API_DOCS_ROUTE } from '../../Config';
|
||||
import { API_DOCS_HOSTNAME, HTTP_PROTOCOL } from '../../Config';
|
||||
import API from 'Common/Utils/API';
|
||||
import { ApiReferenceRoute } from 'Common/ServiceRoute';
|
||||
|
||||
class HelmAPI extends API {
|
||||
class ApiDocsRoute extends API {
|
||||
public constructor() {
|
||||
super(HTTP_PROTOCOL, API_DOCS_HOSTNAME, API_DOCS_ROUTE);
|
||||
super(HTTP_PROTOCOL, API_DOCS_HOSTNAME, ApiReferenceRoute);
|
||||
}
|
||||
}
|
||||
|
||||
export default new HelmAPI();
|
||||
export default new ApiDocsRoute();
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:current-alpine
|
||||
FROM node:current-alpine AS base
|
||||
USER root
|
||||
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-retry-maxtimeout 6000000
|
||||
RUN npm config set fetch-retry-mintimeout 1000000
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
|
||||
@@ -24,7 +27,8 @@ SHELL ["/bin/bash", "-c"]
|
||||
RUN mkdir /usr/src
|
||||
|
||||
# Install common
|
||||
RUN mkdir /usr/src/Common
|
||||
|
||||
FROM base AS common
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
RUN npm install
|
||||
@@ -32,7 +36,8 @@ COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
# Install Model
|
||||
RUN mkdir /usr/src/Model
|
||||
|
||||
FROM base AS model
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
RUN npm install
|
||||
@@ -41,7 +46,8 @@ COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
# Install CommonServer
|
||||
RUN mkdir /usr/src/CommonServer
|
||||
|
||||
FROM base AS commonserver
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
RUN npm install
|
||||
@@ -51,7 +57,8 @@ COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
# Install CommonUI
|
||||
RUN mkdir /usr/src/CommonUI
|
||||
|
||||
FROM base AS commonui
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY ./CommonUI/package*.json /usr/src/CommonUI/
|
||||
RUN npm install --force
|
||||
@@ -59,11 +66,24 @@ COPY ./CommonUI /usr/src/CommonUI
|
||||
|
||||
|
||||
#SET ENV Variables
|
||||
# Install app
|
||||
FROM base AS app
|
||||
|
||||
WORKDIR /usr/src/Common
|
||||
COPY --from=common /usr/src/Common .
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY --from=model /usr/src/Model .
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY --from=commonserver /usr/src/CommonServer .
|
||||
|
||||
WORKDIR /usr/src/CommonUI
|
||||
COPY --from=commonui /usr/src/CommonUI .
|
||||
|
||||
ENV PRODUCTION=true
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
|
||||
@@ -394,7 +394,7 @@ const Settings: FunctionComponent<ComponentProps> = (
|
||||
|
||||
{reseller && (
|
||||
<Card
|
||||
title={`You have purchased this project from ${reseller.name}`}
|
||||
title={`You have purchased this plan from ${reseller.name}`}
|
||||
description={`If you would like to change the plan, please contact ${reseller.name} at ${reseller.description}`}
|
||||
buttons={
|
||||
reseller.changePlanLink
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:current-alpine
|
||||
FROM node:current-alpine AS base
|
||||
USER root
|
||||
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-retry-maxtimeout 6000000
|
||||
RUN npm config set fetch-retry-mintimeout 1000000
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
|
||||
@@ -28,7 +31,8 @@ SHELL ["/bin/bash", "-c"]
|
||||
RUN mkdir /usr/src
|
||||
|
||||
# Install common
|
||||
RUN mkdir /usr/src/Common
|
||||
|
||||
FROM base AS common
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
RUN npm install
|
||||
@@ -36,7 +40,8 @@ COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
# Install Model
|
||||
RUN mkdir /usr/src/Model
|
||||
|
||||
FROM base AS model
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
RUN npm install
|
||||
@@ -45,7 +50,8 @@ COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
# Install CommonServer
|
||||
RUN mkdir /usr/src/CommonServer
|
||||
|
||||
FROM base AS commonserver
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
RUN npm install
|
||||
@@ -54,9 +60,19 @@ COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
#SET ENV Variables
|
||||
ENV PRODUCTION=true
|
||||
# Install app
|
||||
FROM base AS app
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/Common
|
||||
COPY --from=common /usr/src/Common .
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY --from=model /usr/src/Model .
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY --from=commonserver /usr/src/CommonServer .
|
||||
|
||||
ENV PRODUCTION=true
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
|
||||
19
Docs/Installation/Development.md
Normal file
19
Docs/Installation/Development.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Development
|
||||
|
||||
For local development you need to use docker-compose.dev.yml file.
|
||||
|
||||
You need to make sure you have:
|
||||
- Docker and Docker compose installed.
|
||||
- Node.js and NPM installed.
|
||||
|
||||
```
|
||||
# Clone this repo and cd into it.
|
||||
git clone https://github.com/OneUptime/oneuptime.git
|
||||
cd oneuptime
|
||||
|
||||
# Copy config.example.env to config.env
|
||||
cp config.example.env config.env
|
||||
|
||||
# Since this is dev, you don't have to edit any of those values in config.env. You can, but that's optional.
|
||||
npm run dev
|
||||
```
|
||||
61
Docs/Installation/DockerCompose.md
Normal file
61
Docs/Installation/DockerCompose.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Deploy OneUptime completely free with Docker Compose
|
||||
|
||||
If you prefer to host OneUptime on your own server, you can use Docker Compose to deploy a single-server instance of OneUptime on Debian, Ubuntu, or RHEL. This option gives you more control and customization over your instance, but it also requires more technical skills and resources to deploy and maintain it.
|
||||
|
||||
#### Choose Your System Requirements
|
||||
Depending on your usage and budget, you can choose from different system requirements for your server. For optimal performance, we suggest using OneUptime with:
|
||||
|
||||
- **Recommended System Requirements**
|
||||
- 16GB RAM
|
||||
- 8 Core
|
||||
- 400 GB Disk
|
||||
- Ubuntu 22.04
|
||||
- Docker and Docker Compose installed
|
||||
- **Homelab / Minimal Requirements**
|
||||
- If you want to run OneUptime for personal or experimental use in a home environment (Some of our users even have it installed on RaspberyPi), you can use the homelab requirements:
|
||||
- 8 GB RAM
|
||||
- 4 Core
|
||||
- 20 GB Disk
|
||||
- Docker and Docker Compose installed
|
||||
|
||||
|
||||
#### Prerequisites for Single-Server Deployment
|
||||
Before you start the deployment process, please make sure you have:
|
||||
|
||||
- A server running Debian, Ubuntu, or RHEL derivative
|
||||
- Docker and Docker Compose installed on your server
|
||||
|
||||
To install OneUptime:
|
||||
|
||||
```
|
||||
# Clone this repo and cd into it.
|
||||
git clone https://github.com/OneUptime/oneuptime.git
|
||||
cd oneuptime
|
||||
|
||||
# Please make sure you're on release branch.
|
||||
git checkout release
|
||||
|
||||
# Copy config.example.env to config.env
|
||||
cp config.example.env config.env
|
||||
|
||||
# IMPORTANT: Edit config.env file. Please make sure you have random secrets.
|
||||
|
||||
npm start
|
||||
```
|
||||
|
||||
If you don't like to use npm or do not have it installed, run this instead:
|
||||
|
||||
```
|
||||
# Read env vars from config.env file and run docker-compose up.
|
||||
export $(grep -v '^#' config.env | xargs) && docker compose up --remove-orphans -d
|
||||
```
|
||||
|
||||
To update:
|
||||
|
||||
```
|
||||
git checkout release # Please make sure you're on release branch.
|
||||
git pull
|
||||
npm run update
|
||||
```
|
||||
|
||||
OneUptime should run at: http://localhost. You need to register a new account for your instance to start using it. If you would like to use https, please use a reverse proxy like Nginx.
|
||||
23
Docs/Installation/Helm.md
Normal file
23
Docs/Installation/Helm.md
Normal file
@@ -0,0 +1,23 @@
|
||||
### Installation
|
||||
|
||||
```
|
||||
helm install oneuptime ./HelmChart/public/oneuptime -f ./HelmChart/public/oneuptime/values.yaml
|
||||
```
|
||||
|
||||
### Upgrade
|
||||
|
||||
```
|
||||
helm upgrade oneuptime ./HelmChart/public/oneuptime -f ./HelmChart/public/oneuptime/values.yaml
|
||||
```
|
||||
|
||||
### Remove
|
||||
|
||||
```
|
||||
helm uninstall oneuptime
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```
|
||||
helm lint ./HelmChart/public/oneuptime
|
||||
```
|
||||
@@ -10,7 +10,7 @@ To begin with you need to create a custom probe in your Project Settings > Probe
|
||||
To run a probe, please make sure you have docker installed. You can run custom probe by:
|
||||
|
||||
```
|
||||
docker run --name oneuptime-probe --network host -e PROBE_KEY=<probe-key> -e PROBE_ID=<probe-id> -e PROBE_API_URL=https://oneuptime.com/probe-api -d oneuptime/probe:release
|
||||
docker run --name oneuptime-probe --network host -e PROBE_KEY=<probe-key> -e PROBE_ID=<probe-id> -e PROBE_API_URL=https://oneuptime.com -d oneuptime/probe:release
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
<table>
|
||||
<tr>
|
||||
<th rowspan="2">
|
||||
Running mode
|
||||
</th>
|
||||
<th rowspan="2">
|
||||
Rules order
|
||||
</th>
|
||||
<th colspan="2">
|
||||
OneUptime level configurations
|
||||
</th>
|
||||
<th colspan="2">
|
||||
Project level configurations
|
||||
</th>
|
||||
<th rowspan="2">
|
||||
Results
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
Twilio credentials
|
||||
</th>
|
||||
<th>
|
||||
Enabled SMS/Call
|
||||
</th>
|
||||
<th>
|
||||
Twilio credentials + enable “send SMS with Twlio accounts”
|
||||
</th>
|
||||
<th>
|
||||
Enabled SMS/Call alerts (Billing page)
|
||||
</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="4">
|
||||
SAAS
|
||||
</td>
|
||||
<td>
|
||||
1
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
Set
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
Success (without checking daily limits)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
2
|
||||
</td>
|
||||
<td>
|
||||
Set
|
||||
</td>
|
||||
<td>
|
||||
Enabled
|
||||
</td>
|
||||
<td>
|
||||
Unset
|
||||
</td>
|
||||
<td>
|
||||
Enabled - CHARGE FOR ALERTS
|
||||
</td>
|
||||
<td>
|
||||
Success (after checking daily limits)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
3
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
Disabled
|
||||
</td>
|
||||
<td>
|
||||
Failure
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
4
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
Disabled
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
Failure
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td rowspan="3">
|
||||
Enterprise
|
||||
</td>
|
||||
<td>
|
||||
1
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
Set
|
||||
</td>
|
||||
<td rowspan="3">
|
||||
Hidden
|
||||
</td>
|
||||
<td>
|
||||
Success (without checking daily limits)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
2
|
||||
</td>
|
||||
<td>
|
||||
Set
|
||||
</td>
|
||||
<td>
|
||||
Enabled
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
Success (After checking the daily limits)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
3
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
*
|
||||
</td>
|
||||
<td>
|
||||
Failure
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
- In Enterprise Mode - We never charge for alerts.
|
||||
- In SaaS mode - we only charge for alerts ONLY IF global config is used.
|
||||
- Check limits for global config in both Enterprise and SaaS
|
||||
- Ideally in Enterprise Mode - Enable Alerts in Project Billing Page should be hidden. So, you should NOT check if the alerts are enabled or disabled for the project. Just check if the alerts are enabled / disabled in admin dashboard (if using global config). If using local config (project config) - do not check if the alerts are enabled or disabled.
|
||||
|
||||
In general, under the SAAS mode, the SMS/Call alerts can fail for one of the following reasons:
|
||||
1- The custom/global twilio settings are not configured.
|
||||
2- SMS/Call alerts are disabled in the global configurations, and the custom twilio settings are not set.
|
||||
3- SMS/Call alerts are disabled for a project (from billing page), and the custom twilio settings are not set.
|
||||
4- The project's balance is not enough, and the custom twilio settings are not set.
|
||||
5- The targeted phone number doesn't comply with the policy selected in the billing page (high risk countries not selected), and the custom twilio settings are not set.
|
||||
6- the alert phone number is not set (in case of on-call team alerts).
|
||||
7- If the API call fail for any reason (wrong credentials, service down etc...)
|
||||
|
||||
The same reasons, excepet for 3, 4 and 5 , can cause the failure of alerts under enterprise mode.
|
||||
|
||||
Return back to the [main README](../README.md)
|
||||
@@ -1,118 +0,0 @@
|
||||
# Architecture
|
||||
|
||||
## General description
|
||||
|
||||
The main part of the project are:
|
||||
|
||||
- User interfaces,
|
||||
- The probe server,
|
||||
- The backend.
|
||||
|
||||
### User interfaces
|
||||
|
||||
OneUptime has four separated projects for the UI:
|
||||
|
||||
#### accounts
|
||||
|
||||
It's responsible for the registration operations, and the login process in the frontend side.
|
||||
|
||||
#### AdminDashboard
|
||||
|
||||
It's the UI used by the administrators.
|
||||
|
||||
#### dashboard
|
||||
|
||||
It's the UI used by the users.
|
||||
|
||||
#### StatusPage
|
||||
|
||||
It's a public page that renders the reports about the situation of the monitored resources.
|
||||
|
||||
### The probe server
|
||||
|
||||
The probe server is a service that fetches periodically from backend the list of the resources (IoT devices, websites, servers, etc...) that should be monitors. Then, it tries to ping them one by one to report their status to the backend.
|
||||
|
||||
### The backend
|
||||
|
||||
The backend is the heart of the project. In addition to collecting the data from the probe servers, it implements all the other features like managing users, projects, incidents, alerts, etc...
|
||||
|
||||
### The server monitor
|
||||
|
||||
In addition to the three main components, we have the server monitor. This is an agent that needs to be installed in all the servers that will be monitored. Its main task is to collect data, like remaining storage space and CPU load, and to provide them to the probe server when requested.
|
||||
|
||||
The following diagram describes the general architecture of the OneUptime project.
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
### Administrator accounts
|
||||
|
||||
Using the admin dashboard, the administrator will be able to :
|
||||
|
||||
- Manage users,
|
||||
- Manage projects,
|
||||
- Manage probe servers,
|
||||
- Check the audit logs,
|
||||
- Update the software license,
|
||||
- Configure the SMTP server to enable OneUptime sending emails like alerts, invitation, verifications, etc...
|
||||
- Configure Twilio account to enable OneUptime sending alerts SMS/calls,
|
||||
- Configure single sign-on domains to allow users to authenticate using third-party identity providers.
|
||||
|
||||
### User accounts
|
||||
|
||||
The dashboard is dedicated to normal users to allow them to:
|
||||
|
||||
- Manage projects.
|
||||
- Define components.
|
||||
- Build status page.
|
||||
- Check reports.
|
||||
- Invite members.
|
||||
|
||||
#### Components
|
||||
|
||||
A component is one of the resources the user wants to monitor. It can be:
|
||||
|
||||
- A web server.
|
||||
- A server
|
||||
|
||||
In this case, the user will need to install the server monitor agent in the targeted server. The agent can be found in the folder server-monitor of the project.
|
||||
|
||||
- An IoT device.
|
||||
- A script.
|
||||
|
||||
For every component, the user can define the different criteria that allow the backend to decide whether this component is working properly, degraded, or down.
|
||||
|
||||
In addition to the name, type, and criteria, the user can choose the category to which the resource should belong. The categories can be managed through the same dashboard under Project Settings.
|
||||
|
||||
#### Status pages
|
||||
|
||||
The user can create many status pages. For each page, he can select the resources that should be represented. The status page will have a public link that anyone, even unauthenticated users, can use to view the situation of the resources.
|
||||
|
||||
#### Reports
|
||||
|
||||
OneUptime generates several reports related to the incidents, the average resolve time, and monitors.
|
||||
|
||||
#### Inviting members
|
||||
|
||||
The user can invite by the email other users to join the team.
|
||||

|
||||
|
||||
## OneUptime in production
|
||||
|
||||
OneUptime can be deployed in one of two modes: SAAS mode and enterprise mode. SAAS is the mode used by Hackerbay to run OneUptime. The clients can create accounts without being administrators or having access to the admin dashboard.
|
||||
In enterprise mode, the client will have the oneuptime code deployed on his personal servers.
|
||||
|
||||
The running mode needs to be configured on the following subprojects, by updating the .env file in each of them, as the behavior of some of their features may change depending on the mode.
|
||||
|
||||
- accounts
|
||||
- backend
|
||||
- dashboard
|
||||
|
||||
### Some of the differences between SAAS and Enterprise mode
|
||||
|
||||
#### user registration
|
||||
|
||||
In SAAS, when a user submits the first form with his details: name, email, password, etc... to signup, a second form will show up to collect the credit card details. After submitting that form, the user will receive an email that contains the details to activate his account.
|
||||
|
||||
In enterprise mode, Only the first account in the database can be created using the signup form. Once created, the signup form will not be accessible. The initial account will be assigned the role of master administrator, and it'll have the privileges to create other new accounts.
|
||||
@@ -1,9 +0,0 @@
|
||||
# Concept.
|
||||
|
||||
Please read OneUptime public website (https://oneuptime.com).
|
||||
You can also run oneuptime website locally by running
|
||||
|
||||
- `npm install` and
|
||||
- `npm start`
|
||||
|
||||
inside of the `home` folder of this project.
|
||||
@@ -1,7 +0,0 @@
|
||||
# DNS
|
||||
|
||||
Here are all the active DNS we use in prodiction:
|
||||
|
||||
- oneuptime.com / www.oneuptime.com: For helm production service.
|
||||
- staging.oneuptime.com: For staging service.
|
||||
- charts.oneuptime.com: Linked to oneuptime.com | www.oneuptime.com but used specifically for helm charts.
|
||||
@@ -1 +0,0 @@
|
||||
<mxfile host="app.diagrams.net" modified="2020-12-11T08:59:50.389Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" etag="pGpaML1Jgff4x4CsnkXM" version="14.0.1" type="google"><diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">5VtZd5s4FP41PvOUHECI5dFOnDbTtMnUbTJ5miODbGuCkQdwY/fXjwRiEfJCamOSNA8xumhB997vboIeuJivPkRoMftMfRz0DM1f9cBlzzAM03HZD6esM4ru6mZGmUbEF7SSMCI/sSBqgrokPo6ljgmlQUIWMtGjYYi9RKKhKKLPcrcJDeRVF2iKFcLIQ4FKfSB+MsuojmGX9I+YTGf5yroldjxHeWexk3iGfPpcIYFhD1xElCbZ1Xx1gQPOvZwvD9frh+Dmyfrw51/xf+j74NO3L/dn2WRXLxlSbCHCYfLLU2uP2nfb7bvrxSW8CejdDYwHYoj2AwVLwa9vH4eMcHs//Hp/PXzgl1fs38dbfnn1eH3H7z7cfv00EkxJ1jmnGX8W/DKkCfsZxElEnwqeM24NJjRMhIIwJWI9smvGTDB4npEEjxbI45RnpouMNkvmAe/Lh5IguKABjdKlwGQyMTyvWKRyx7fGFrTEYhW6xf40vpDYMI4SvKppyB726oXMGVowneMkWrNxYpYz3RTMFEg5Mx1BeC71zsg7zSo6l/dDQtWnxeSlONmFkOgLpGsqMsI+A4do0iiZ0SkNUTAsqYOILkMf81k5t8o+N5QuhDD+xUmyFoJEy4TKosrW5Asdhb0RDlBCfsiTbeKUGHpHCVumEAtwDEkqpu2cQ3mSBEVTnIhxVQDtm0qz5Iliuow8rEyUiq7Y0QHStN6cONnDpjzZsSsgfEIqhF27t1vRD24UqkIFEDTSjmMJFULFAv/OUoYHCvkgcwlUZzjDPBKgIUmYI2HPn7ZJ6LGYJtWiMQ5oOI3ToIbfidM12D0yIUwedUnu8XJt+SbDhbKSa6pn0m1L9Ux63cQdjded2DLGw2j9Nx/PfIBoPorp0sblSmqtq607HBG2dxwJ4oGQ2QsFXdss00MdomtKumC5tejjV90hdMy6Z23ZIaqmM8Nr7M2wvwxwnKYVQcDSCkLDHJ5MQDHOATshoZ/imqY0loTEJE5IOGUNOikQX1iAl+GZzCtZh8OVxieReBoeJ0f8RtaryEGMNoNUw5GFX4mGqqbAdU5pCgz4Hj1env/ux7nZpc/T1QzwKnV1Q+TN2M9IYIlnf5zliqjUNK/C5DwlnK+mvLxwPgnoszdDUXIeMDn9E5A5SbYkf21BAADZbFlwgy/UNmRpVltZmv4+9f9Qtd4SrxuuJD4HNHNfx/I5hoqXx6FaC+F8vUEsOpSZjgIy5abfYzzmgcSA6zfxUNAXN+bE9zP54pj8RON0Pi7hBd9QukU46MHLXQARlTQxuFfUr6qi3aGI2yscLGrSocz+MwGmX5Vq3oVOJjFuR2DOW4o0T4PKBl7JAO1En7Yj6Y+uwRPj11Xw++X2fcA30/Rd8LU125bZ/9rRm+trVVwY+zyOH8aM8SiLpa+OFZb42CNxOmWnQYljNgxKCuLxoxL7zUUlLVirWrBR53bTXBlaeyZqOVPWLQVGAj5YkbIsw44qVtCWy7K2vQEQximjdLOTKL2MI6QoogwqTlix2htcGI1r+Vs81YFwNUDtBE5vWNvqRxFaV7oJb711Ib12aKDnhG34rw84A5ZW08jsGY4b66gF7RFO6139gCE2fp3QB4bsC3Wtc+yDTg5SKzmEaYGu69X7j3haqlfb8Nx0bDf/c4CkHaChSz4WpoCa//cMK+BVrHiBQnY95df9tNxcnhOllWcvwszh+nl/tn51yCtFo1ZL2Da81KADcEIw5gnj20jou4EiPJUjPsysqgc4I/bIyQ4k6B0iQbfO5WNUpopdeybr7WVpTfTXaqi/1qF532H6q2ZVdxEdp0eQOGIayHlCstcDUG/7KeLrMPR6LUQ1NoVdJ7X0pq3ahyx+HS3HsReRccri1xzMQkfmKshj8u7yWPc9WgzTaWgxMsx2ZTHyx1ROW/s/EAnSkq+hfc7MhKrRb+6o1VA8JnA3GJWTnrZCNSnedoD3SkPKtrEEm76hdJyM76XFF5YQSjqlg921lz392ym9wE6sbKFReu/waiFekaSYi11XpmKtcibeWFcap052GqtrS1XHeqm6taqjacqHd6axU++hqx3S/TQlSkstp7yX49jMBOw6jrUglCWaf7BxoEaemfDcrr2K2d4RbY6/PRWxAfKesuDdm2HvKX1RepLuNV7QMH37co4Sdi8uM6U/sqoZi1gigtJT3maVs3YOf1G8yD5Qm5AVt9KnS8XApkysiK2k8+CSfPxaw2//zvZeX2Q19UWw08KFpUI2L1ww6xlw1I0LuPbvrhW5d1ajcOsJhb7p9c0jJdSsWX7dmZnL8iNZMPwf</diagram></mxfile>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB |
@@ -1,18 +0,0 @@
|
||||
# How OneUptime Works
|
||||
|
||||
## The overview of how OneUptime works
|
||||
|
||||
One of the main purpose of OneUptime is to monitor resources. The flow of how this is done is depicted in the flowchart below.
|
||||
|
||||
## Description
|
||||
|
||||
1. The probe server has a cron job running that pings all available monitors at a time interval.
|
||||
2. After the probe server pings a monitor, it calls the backend API along with the response from the monitor.
|
||||
3. A monitor contains criteria for an UP, Down, and Degraded statuses. The backend checks if the response from the probe matches any of these criteria.
|
||||
4. If there is a match, an incident is created.
|
||||
5. External subscribers added to the monitor are alerted via SMS, Webhook or Email alerts.
|
||||
6. All On-call schedules that are associated with the monitor are then identified.
|
||||
7. Each schedule contains an escalation policy which defines which team will be alerted for the incident.
|
||||
8. The members in the team will be alerted via SMS, Call and/or Email.
|
||||
|
||||

|
||||
@@ -1,17 +0,0 @@
|
||||
# Introduction
|
||||
|
||||
OneUptime is one complete Observability platform.
|
||||
|
||||
OneUptime lets you do:
|
||||
|
||||
**Monitoring:** Monitors your website, web apps, APIs, servers and more and give you detailed metrics of things that might be wrong with your infrastructure.
|
||||
|
||||
**Status Page:** OneUptime gives you a beautiful and customizable status page for your online business which helps improve transparency with your customers and cuts support costs.
|
||||
|
||||
**Tests:** Write automated tests for your website, API's and more and know instantly when they start failing.
|
||||
|
||||
**On-Call and Incident Management:** On-Call Management lets you alert the right team at the right time saving you critical time during downtime.
|
||||
|
||||
**Performance Monitoring:** Monitor the performance of your apps, servers, APIs, and more and alert your team when any of your performance metrics degrades.
|
||||
|
||||
**Website:** https://oneuptime.com
|
||||
@@ -1 +0,0 @@
|
||||
<mxfile host="app.diagrams.net" modified="2020-06-05T17:09:47.701Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36" etag="hbal5e5wRNsxc2uXYzzf" version="13.1.14" type="device"><diagram id="P0tG3eMJVzySH8hgiAyU" name="Page-1">7Vtbc9o6EP41PIbxHfMYQmh7mpxmDp3Tnr5khC1AjbF8ZEGgv74rW8IyNgQIt3TSzDTWWlrJ2m+/XV3SsG8m8w8MJeN7GuKoYRnhvGF3G5ZltkwffgnJIpc4jpsLRoyEslIh6JNfWAoNKZ2SEKelipzSiJOkLAxoHOOAl2SIMfpcrjakUbnXBI1wRdAPUFSVfiMhH+dS3zUK+UdMRmPVs2nINxOkKktBOkYhfdZE9m3DvmGU8vxpMr/BkZg8NS95u96at8uBMRzzbRpctXr3P+IkYfGPYfdf9Bn/s1hc2XJsfKE+GIfw/bJIGR/TEY1RdFtIO4xO4xALrQaUijp3lCYgNEH4E3O+kMZEU05BNOaTSL7Fc8K/a8//CVVNV5a6c6k5Kyy0wgNmZII5ZkoWc7b4rhc0TaJYqMpKC720qizliPFrgRgQxDTGStYjUbRsGKoaQYTSlAS5UFYR35PPp5jEtWaSopROWSBrfZ2gvz7/P7t7jO/+/vhj+gFFZHrlSLgjNsJ8gw3NJZjACzGFj2ILaMdwhDiZlceBpDuMlvUKxMCDBM0OAHLOAqALNLwCtdFst60SsFu+szO0zwAj65Uwkk0fKIERWoaMALYv6U/yv20bZRX5+GWrAoww3WihVUtEhXRDP0a5H6vVLrPhC/XtlrniC/kICs9Yzsn+zmLL6ZuhaContM8Rn6bZPCfgCRu9SYLzYPx7ZTQNwythtW2296dhs+yNuRv8kTxcW880Gqch4k2j1KD1wOgAiw/EbAbTu4oslCZ5wjQkc4GmTuZimN3OsPC0fBqXSYuY9hCl4yXuNDylnNEnfEMjygqbRWiAoweaEk5oLAyF48zIHRgLJ5Bg3a1UGFDO6USrcB2RkXjBBaw7SJaWeoZgbNVnQ/iz6fvdfMiJ+MTJfCRS0uYkDRBuZq0SRlLczOfjcYRjgF6wFhdiGHiuiaqWlW8tZ4V7VPm5yBgtaZ+xliy6xpGw0K5g4RxRuuTJhZ/qzmyWnVlCp/DkMr0AW/llirF8fx+O2Z8L/C25wKnln7UIAiZ2fNfLGx044lquU1aRD/7gEddsuTtFXDWu2ohbtFbDocNhinlj1TV2i8qbbKoHZcmYxoTGhNN37jwed9quXUaRezrurE+Dq+R5R1KBRDrMnDPnCJGyNSwvguF0BgAQbySegALXg+YYqdzlZUtq42Y7ijxXtqRGqVl5gIInmJV3X1e+HiKOBijFj3oC+Spnb7+cJ9n+CX29uhz7hgfvGXMFCShJItAvupVgsNaYvgYg66nfsZvui4Bw7FOSfwUQn758reDgbZvdNcTPWrPDp01jcH48IwF+HEGESg5i65XtFsdVttdsrex6GltXV8wqoE8jFdCVJCJKcjPGwZOIb+Nsba22b7LkIJdpKULeGIZXtNeEejdnXp7tnzoouEV4WHzJrplErYHa1USinsW9jdCDtVW7ZfgrWWbjVUutgy5KNn28Bs5pDDYcwywKJgbDW8YUuDg9MjutI49Dsxad8ojE0Ls6xjPqqGyVsmD+gaWa2URk/y8R+Cq2ulruUiu6qlmVON4pyaqaqmxDVvcoFuebEirNjYRUapU1Shj9CcbYpR1SDbWdx93bQ7KB4xRv3RBQMySjKRNt7xGJUi2HM4ZiKWagCACxl8KvzyQitKqxf98HLTcoM95u6pcf2u9/EY0LvwZf2azk7GFj3dmbY7T22Yq7+ChkqlsHL4ahNQvaNx2GzCrvoHBCYgLBAtVtclx2bny4KLMSFy2jZXX8I0Ufs25ZdNLoc55rInue8p91T6zei6xtOcStR8Zp7nLY7uuSjOViSNyK2j0Y6lur2UZqlkPI3VQcbq1Pbb8aJA5IKHho26af4hnholMKAZntmjZ9AvoAwigpSMBukC5M0mXaoAa1e04iF55oBhkOGkQ7J1ovpw4Zm5adcCWwrtLqhIRh7u04Jb+yUeUOJk+PQLXbabjdTcwor+7Jxo3lhTnd0zbQ0qaIa9lt53UhVulSeha1Co4YgKtbUlkEeo+7Wdxt9TrXjnWouFs5x3dqNqlOG3m9jZH3YDeCLvBC3SHj78Vd4dk4TM3Xr0WyDSLhvQOKWPV4SjkNmWQ3mXWr1vviiy6cOX8HBU+jDFeatw2zf1Al6+xaHYdkzrx6NiLH0x1zLq5qX4upsXpBGFtNEojlNSCWNQPo0eqJkyb4JeQQKHsJPERXM9AnImbPFtOaBgzj+NHh1AZO9JtJPKqygW17nmEciA3M1STcrFCB266hgvaxqKB1WioQtwMtr8wH3l7XbS6QENy3QQjVhLz7TgRnJoK6Q8rTEoF/4pygygO+94fwgPc2eMCr8EBfX2u/c8GZuGDNMfZh2ACKxV+H5evJ4m/s7Nvf</diagram></mxfile>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
@@ -1,21 +0,0 @@
|
||||
# Description of the subprojects in this repo.
|
||||
|
||||
- `accounts` - A React project used for Authentication (Log in, Sign up, Forgot Password, etc.)
|
||||
- `dashboard` - A React project for OneUptime user where user can interact with the OneUptime platform.
|
||||
- `admin-dashobard` - React Project where admin can block users, delete projects and more.
|
||||
- `ApiReference` - HTML/CSS project. A public reference of OneUptime documentation.
|
||||
- `backend` - NodeJS Service. It's OneUptime API's.
|
||||
- `home` - HTML/CSS. Home Page / Marketing page of OneUptime.
|
||||
- `HttpTestServer` - A test server used to test website monitors for OneUptime.
|
||||
- `ci` - DevOps/CI/CD scripts.
|
||||
- `marketing` - This is where you'll find logos, brief description of OneUptime, etc.
|
||||
- `certifications` - SOC/ISO/PCI certifications and more.
|
||||
- `postman-collection` - Postman collection for OneUptime API.
|
||||
- `probe` - Probe is an agent that gets installed on a third party server on a thir party datacenter and it monitors users websites, services, from that data center. You can deploy multiple probes to monitor users resources - A probe in a datacenter in EU, in US, etc.
|
||||
- `server-monitor` - A probe that gets installed on a server and that monitors that particular server.
|
||||
- `tests` - Smoke test that is executed after OneUptime is deployed to staging or production. If smoke test fails, the staging / production deployment will automatically be rolled back.
|
||||
- `StatusPage` - React project - Status page project of OneUptime.
|
||||
- `zapier` - OneUptime integrates with zapier. This is where integration code is. This gets deployed to zapier directly.
|
||||
- `InitScript` - a container that runs schema migration script.
|
||||
- `HelmChart` - Helm Chart Scripts for installation of OneUptime into Kubernetes.
|
||||
- `kubernetes` - Values of Kubenrets env
|
||||
49
Docs/run.md
49
Docs/run.md
@@ -1,49 +0,0 @@
|
||||
# Running OneUptime
|
||||
|
||||
## Running this project for local environment
|
||||
|
||||
- Make sure Docker and Docker Compose is installed.
|
||||
- Make sure Node and NPM are installed.
|
||||
- Run: `npm run dev`
|
||||
- Docker will build and run containers. This will take some time.
|
||||
- You should see OneUptime Home Page on `http://localhost`
|
||||
- The containers are hot reloadable, so any changes you do in local development, it should auto-restart.
|
||||
- However, containers don't auto-restart for changes made to `package.json` / `webpack.config.js` files. If you've made a change to those files, you should build the container again by running:
|
||||
```
|
||||
npm run force-build-dev <application_name>
|
||||
|
||||
Example:
|
||||
npm run force-build-dev accounts
|
||||
```
|
||||
- After force build completes, you can run it again by running `npm run dev`
|
||||
|
||||
### Logs
|
||||
|
||||
- To check the logs of the application, you should:
|
||||
|
||||
```
|
||||
npm run logs-dev <application_name>
|
||||
|
||||
Example:
|
||||
npm run logs-dev accounts
|
||||
```
|
||||
|
||||
### Debugging
|
||||
|
||||
- Debugging with breakpoints are supported.
|
||||
- You should use VSCode for the best debugging experience.
|
||||
- Select the Debug item on the left menu of VS Code and then you should see list of various apps that you can debug. You can pick any and press the green button in VS Code which attaches the debugger.
|
||||
- If you make any change to the file, container automatically restarts and the debugger connection is broken. so, you need to press that green button again to debug.
|
||||
|
||||
### Clear cache and Prune
|
||||
|
||||
- Sometimes you need a clean system for various reasons with a clean docker images and clean cache.
|
||||
- In those cases, you can prune docker by running `npm run prune`. Please note this will also delete images that are not related to OneUptime project.
|
||||
|
||||
## Running on: on-prem, staging, or production.
|
||||
|
||||
### Running with Docker Compose:
|
||||
- Run `docker compose up`
|
||||
|
||||
### Running with Kubernetes and Helm
|
||||
- Please check `README.md` in the `HelmChart` folder.
|
||||
@@ -1,53 +0,0 @@
|
||||
# Running Tests
|
||||
|
||||
## Introduction
|
||||
|
||||
Tests are in the `/tests` folder.
|
||||
|
||||
There are two types of tests,
|
||||
|
||||
- SaaS
|
||||
- Enterprise
|
||||
|
||||
### SaaS tests
|
||||
|
||||
This runs the application in SaaS mode. What is SaaS mode? SaaS mode enables plans and pricing with stripe. It runs the test as if its a hosted OneUptime service on oneuptime.com.
|
||||
|
||||
#### Running tests in SaaS mode
|
||||
|
||||
```
|
||||
npm run docker-saas-test
|
||||
```
|
||||
|
||||
This spins up a new local OneUptime cluster on Docker Compose and runs a test on it.
|
||||
|
||||
### Enterprise tests
|
||||
|
||||
This runs the application in Enterprise mode. What is Enterprise mode? Enterprise mode DISABLES plans and pricing. It runs the test as if its a hosted on an on-premise datacenter with an enterprise.
|
||||
|
||||
#### Running tests in Enterprise mode
|
||||
|
||||
```
|
||||
npm run docker-enterprise-test
|
||||
```
|
||||
|
||||
This spins up a new local OneUptime cluster on Docker Compose and runs a test on it.
|
||||
|
||||
### Debugging tests
|
||||
|
||||
To debug tests you first need to run the cluster and then run the tests seperately.
|
||||
|
||||
```
|
||||
npm run docker-saas # Running a cluster in SaaS mode, or...
|
||||
npm run docker-enterprise # Run a cluster in enterprise mode.
|
||||
```
|
||||
|
||||
Once the cluster is running, you can run tests like:
|
||||
|
||||
```
|
||||
export SLOMO=20
|
||||
export HEADLESS=false
|
||||
jest ./saas-tests/StatusPage/StatusPage.test.js # or any file.
|
||||
```
|
||||
|
||||
There's also a .vscode/launch.json in test folder which will help you to debug tests with vscode.
|
||||
@@ -1 +0,0 @@
|
||||
<mxfile host="app.diagrams.net" modified="2020-06-05T17:11:07.152Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36" etag="8O-XtXlOKaga96iVvsNe" version="13.1.14" type="device"><diagram id="3j40zMsNCfodiNM318Qs" name="Page-1">7Vtbb+I6EP41SOc8BOVCLjwCbbfVnmp7SrX7WJnEEJ8mdtZ2CuyvP3YSc4nTbqslJUW0Egpjx7Hn+2Y8Mw49Z5KuvlCQxbckgknPNqNVz7no2XbgBeJTCtalYDBwS8GCoqgUWVvBFP2CldCspDmKINvryAlJOMr2hSHBGIZ8TwYoJcv9bnOS7D81AwuoCaYhSHTpDxTxuFqWa27l1xAtYvVky6xaUqA6VwIWg4gsd0TOZc+ZUEJ4eZWuJjCRulN6Ke+7eqF1MzEKMX/LDWD6PYgvH6yn26/uanIT3Y9+/WsoNT+DJK9WfEfJf1KT5aT5WmmCkhxHUA5m9pzxMkYcTjMQytalgF7IYp4m4pslLhMwg8kYhE+L4rYJSQgthnHmxZ/oMieY78jN4k/InyHlSAAwStACi7YZ4ZykoqGaqWiGqxd1YG00KxgJSQo5XYsu1Q22Qme9z7LlFlpPQRvvwOoElRBUdFpsht5qXFxUSn8HAENN/7cEI04oE9K/QpJmBIvFsb9PEo/B8Ld4OF4DHhujOjgelqcBomle6jB7cfWVJwIz1d18r1Zcq6aVoaYVK2jQiuW3pZVAU8oEcLggxawtTUFiROGfxZexcHqZFIYJyaPf0/TdHGRiKIQXDySTI5hvY/qbiPsKO3TgjoeMTtcLILgHhPZtLxGPH8+ouFrIKwapWLSGVkYQ5pBePks/UwGx2aykSiPA4o2j2UGLcUqe4B5e1sV4okC4IwxxRCReIZRP2AHyn1qHDaB1pLlEdkxyniAsHqU2eTkTUHXZDK7Ilq4WMhbpk/kchbBfLpv1s3jN5NCPMWFSHXOUJHuTd034miM8PG2Uve+bu6s7QdvXOeXaLVHK1yi1hDNJmTN9Okkfy+4WfyydQB++hXp+17ZQa/DaHqpr6HT3UL9re6jlatDwGNGoXA2Xkxvd3Zwd38c7vheoUg3je323W55Pz+amHPCcVZUCpnHoFJK4NyTV9rDJoFtL4tTADVGxDgFgWWkbc7SSSIwPZNdYJO4tmLRmvZo5WkFw0WDVKQsB7Bd3ZRQx2M8gnROaAhzCx7QsOvwZyQ7BpVrq6zgalxrrM15bTLI0Jomt4EyibpNo2DES2S9vC2Su5+lhGRci6axkdUVr31QFzzzsMg+drvFQT0HuYUZoA49OMi5xjh+Y6JmGMm+RhWGZ+iaAlU/1fubyaGgsgncuVnwNQQSpwREXGNnmA1xxI5QaNQyEY0gRV9IIsSwBaymXwb8SSwjk+ZpR1BO2sh/Fyg0jhRHKU9Ui77yuWuxASfk6g3NBA8MoK4ylVDLCKD63s1a+aiQglEdsUqV1P3YPGUmei2FQuqlXCsWWuig7nV2ccnGVW3ukOcYiX38EmTwDPfZeWys+WQ05WKOTG7ZlYk0HOCdtYjc4RFEZDZwt6PNZkFPfpo5uQU3125O2oG88lgcb9f2JquDI7Pf7evPZ3D6juQ06Z2764foDBJLjKUxnkJ5mcF5/88NuCM4bX/2wBq0F53r19juCy/aOPFsy4D8q+udMlvzlp275tunb4+DoFuzWQk7b16kz8HTmuG0Rx9HLzbeF6Z6JU6LvX41HA/v4xAn8jhGnobocpQgjATeQFax2+VNDyTXlf6d5dVz+eG7X+KMXlr8t8dnvKPAvRgPT9o7OG1+FeJ3hjdOQ4R0pyDyAfuuF3sb3AD620OvopXaWy5fosvLtej2oP2ETDQkOYcZZX/76osOBZf3w2fYDjUdNqaH6ScvhWaTXMi9TIG67mt5Oe7YHUokNnrFspxyxe06I52iRi1BCYNw24z5FMKEzsVu1v+MzTq/9VcUxc49NOtkiyAFKzn6tg3lP/RxaBCTqxbnD80x83f6mrWjb+WGgc/k/</diagram></mxfile>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB |
@@ -3,10 +3,14 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:current-alpine
|
||||
FROM node:current-alpine AS base
|
||||
USER root
|
||||
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-retry-maxtimeout 6000000
|
||||
RUN npm config set fetch-retry-mintimeout 1000000
|
||||
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
|
||||
@@ -28,7 +32,8 @@ SHELL ["/bin/bash", "-c"]
|
||||
RUN mkdir /usr/src
|
||||
|
||||
# Install common
|
||||
RUN mkdir /usr/src/Common
|
||||
|
||||
FROM base AS common
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
RUN npm install
|
||||
@@ -36,7 +41,8 @@ COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
# Install Model
|
||||
RUN mkdir /usr/src/Model
|
||||
|
||||
FROM base AS model
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
RUN npm install
|
||||
@@ -45,19 +51,28 @@ COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
# Install CommonServer
|
||||
RUN mkdir /usr/src/CommonServer
|
||||
|
||||
FROM base AS commonserver
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
RUN npm install
|
||||
COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
# Install app
|
||||
FROM base AS app
|
||||
|
||||
WORKDIR /usr/src/Common
|
||||
COPY --from=common /usr/src/Common .
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY --from=model /usr/src/Model .
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY --from=commonserver /usr/src/CommonServer .
|
||||
|
||||
#SET ENV Variables
|
||||
ENV PRODUCTION=true
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
|
||||
@@ -2,6 +2,9 @@ FROM node:18.13.0-alpine
|
||||
USER root
|
||||
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-retry-maxtimeout 6000000
|
||||
RUN npm config set fetch-retry-mintimeout 1000000
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Pull base image nodejs image.
|
||||
FROM node:current-alpine
|
||||
FROM node:current-alpine AS base
|
||||
USER root
|
||||
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-retry-maxtimeout 6000000
|
||||
RUN npm config set fetch-retry-mintimeout 1000000
|
||||
|
||||
ARG GIT_SHA
|
||||
ARG APP_VERSION
|
||||
|
||||
@@ -20,7 +23,8 @@ SHELL ["/bin/bash", "-c"]
|
||||
RUN mkdir /usr/src
|
||||
|
||||
# Install common
|
||||
RUN mkdir /usr/src/Common
|
||||
|
||||
FROM base AS common
|
||||
WORKDIR /usr/src/Common
|
||||
COPY ./Common/package*.json /usr/src/Common/
|
||||
RUN npm install
|
||||
@@ -28,7 +32,8 @@ COPY ./Common /usr/src/Common
|
||||
|
||||
|
||||
# Install Model
|
||||
RUN mkdir /usr/src/Model
|
||||
|
||||
FROM base AS model
|
||||
WORKDIR /usr/src/Model
|
||||
COPY ./Model/package*.json /usr/src/Model/
|
||||
RUN npm install
|
||||
@@ -37,7 +42,8 @@ COPY ./Model /usr/src/Model
|
||||
|
||||
|
||||
# Install CommonServer
|
||||
RUN mkdir /usr/src/CommonServer
|
||||
|
||||
FROM base AS commonserver
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY ./CommonServer/package*.json /usr/src/CommonServer/
|
||||
RUN npm install
|
||||
@@ -45,7 +51,21 @@ COPY ./CommonServer /usr/src/CommonServer
|
||||
|
||||
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
# Install app
|
||||
FROM base AS app
|
||||
|
||||
WORKDIR /usr/src/Common
|
||||
COPY --from=common /usr/src/Common .
|
||||
|
||||
WORKDIR /usr/src/Model
|
||||
COPY --from=model /usr/src/Model .
|
||||
|
||||
WORKDIR /usr/src/CommonServer
|
||||
COPY --from=commonserver /usr/src/CommonServer .
|
||||
|
||||
|
||||
ENV PRODUCTION=true
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
|
||||
35
HelmChart/MicroK8s.md
Normal file
35
HelmChart/MicroK8s.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Introduction
|
||||
|
||||
|
||||
### Installation
|
||||
|
||||
Install Microk8s: Read the [installation guide](https://microk8s.io/docs) for more details.
|
||||
Install Helm
|
||||
|
||||
Run
|
||||
|
||||
```
|
||||
sudo microk8s kubectl config view --raw > ~/.kube/config
|
||||
```
|
||||
|
||||
|
||||
### Unistallation
|
||||
|
||||
```bash
|
||||
microk8s uninstall
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
- launch failed: instance "microk8s-vm" already exists (on MacOS)
|
||||
|
||||
```bash
|
||||
multipass delete microk8s-vm
|
||||
multipass purge
|
||||
|
||||
# reinstall
|
||||
microk8s install
|
||||
microk8s status --wait-ready
|
||||
```
|
||||
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
# Helm Chart for OneUptime
|
||||
|
||||
This project is deprecated. New helm charts will be launched soon.
|
||||
## Introduction
|
||||
|
||||
This chart bootstraps a [OneUptime](https://oneuptime.com) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
|
||||
|
||||
## Adding Dependencies
|
||||
|
||||
- Add dependencies in Chart.yaml and run helm dependency update.
|
||||
|
||||
## Addons for MicroK8s
|
||||
|
||||
- Hostpath Storage if you're using one node.
|
||||
|
||||
```
|
||||
microk8s enable hostpath-storage
|
||||
|
||||
# then you should see storage class name
|
||||
kubectl get storageclass
|
||||
|
||||
# You can then use this storage class to run this chart
|
||||
```
|
||||
|
||||
By default, the hostpath provisioner will store all volume data under /var/snap/microk8s/common/default-storage
|
||||
|
||||
To customize the default directory, please read the docs here: https://microk8s.io/docs/addon-hostpath-storage
|
||||
@@ -1,39 +0,0 @@
|
||||
apiVersion: v1
|
||||
entries:
|
||||
OneUptime:
|
||||
- apiVersion: v2
|
||||
created: "2021-09-10T14:13:26.551683+01:00"
|
||||
dependencies:
|
||||
- name: nginx-ingress-controller
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
version: 6.0.1
|
||||
- name: redis
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
version: 14.8.8
|
||||
description: One complete DevOps and DevOps platform.
|
||||
digest: 84b0d954025bc9d98419d3f7d4ee9e4470db717297e4fc1aaa546af75b16a96a
|
||||
home: https://oneuptime.com
|
||||
icon: https://oneuptime.com/img/ou-bb.svg
|
||||
keywords:
|
||||
- DevOps
|
||||
- IT DevOps
|
||||
- DevOps
|
||||
- Monitoring
|
||||
- Status Page
|
||||
- On-Call
|
||||
- On-Call Management
|
||||
- Incident Management
|
||||
- Performance Monitoring
|
||||
- API Test
|
||||
- Website Monitoring
|
||||
- Website Test
|
||||
- SRE
|
||||
maintainers:
|
||||
- email: support@oneuptime.com
|
||||
name: OneUptime Support
|
||||
url: https://oneuptime.com/support
|
||||
name: OneUptime
|
||||
urls:
|
||||
- OneUptime-4.0.0.tgz
|
||||
version: 4.0.0
|
||||
generated: "2021-09-10T14:13:26.53643+01:00"
|
||||
1
HelmChart/public/oneuptime-deprecated/.helmignore
Normal file
1
HelmChart/public/oneuptime-deprecated/.helmignore
Normal file
@@ -0,0 +1 @@
|
||||
.git
|
||||
9
HelmChart/public/oneuptime-deprecated/Chart.lock
Normal file
9
HelmChart/public/oneuptime-deprecated/Chart.lock
Normal file
@@ -0,0 +1,9 @@
|
||||
dependencies:
|
||||
- name: nginx-ingress-controller
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
version: 6.0.1
|
||||
- name: redis
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
version: 10.5.11
|
||||
digest: sha256:a75a0ffb78f91da3da43a6808df7c9c1d8a5736f512f8e15f6ff67775d3b9ecb
|
||||
generated: "2021-10-05T18:40:47.90754+01:00"
|
||||
33
HelmChart/public/oneuptime-deprecated/Chart.yaml
Normal file
33
HelmChart/public/oneuptime-deprecated/Chart.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
apiVersion: v2
|
||||
name: OneUptime
|
||||
version: 4.0.0
|
||||
description: One complete DevOps and DevOps platform.
|
||||
keywords:
|
||||
- DevOps
|
||||
- IT DevOps
|
||||
- DevOps
|
||||
- Monitoring
|
||||
- Status Page
|
||||
- On-Call
|
||||
- On-Call Management
|
||||
- Incident Management
|
||||
- Performance Monitoring
|
||||
- API Test
|
||||
- Website Monitoring
|
||||
- Website Test
|
||||
- SRE
|
||||
home: https://oneuptime.com
|
||||
dependencies:
|
||||
# https://github.com/kubernetes/ingress-nginx/tree/master/charts/ingress-nginx
|
||||
- name: nginx-ingress-controller
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
version: "6.0.1"
|
||||
- name: redis
|
||||
version: "10.5.11"
|
||||
repository: "https://charts.bitnami.com/bitnami"
|
||||
maintainers:
|
||||
- name: OneUptime Support
|
||||
email: support@oneuptime.com
|
||||
url: https://oneuptime.com/support
|
||||
icon: https://oneuptime.com/img/OneUptime.svg
|
||||
engine: gotplhelm dependency update
|
||||
85
HelmChart/public/oneuptime-deprecated/templates/NOTES.txt
Normal file
85
HelmChart/public/oneuptime-deprecated/templates/NOTES.txt
Normal file
@@ -0,0 +1,85 @@
|
||||
============================================
|
||||
IMPORTANT: After Installation Steps
|
||||
============================================
|
||||
|
||||
** Thank you for installing OneUptime **
|
||||
** Please be patient while the chart is being deployed **
|
||||
** This usually takes few minutes or more **
|
||||
|
||||
To access your OneUptime app from steps below:
|
||||
|
||||
{{- if eq (index .Values "nginx-ingress-controller" "service" "type") "LoadBalancer" }}
|
||||
============================================
|
||||
Make sure external IP's are assigned.
|
||||
============================================
|
||||
|
||||
Please run these commands to get OneUptime URL
|
||||
|
||||
$ kubectl get svc {{ $.Release.Name }}-nginx-ingress-controller --namespace={{ $.Release.Namespace }}
|
||||
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
If the load balancer did not assign an external IP yet (if the IP is still pending).
|
||||
Retry this command after few mins.
|
||||
{{- end}}
|
||||
============================================
|
||||
Sign Up
|
||||
============================================
|
||||
|
||||
{{- if eq (index .Values "nginx-ingress-controller" "service" "type") "LoadBalancer" }}
|
||||
Go to the External IP (generated from step 1) from your browser and sign up a new admin account.
|
||||
{{- else}}
|
||||
Go to the External IP of your server from your browser and sign up a new admin account.
|
||||
{{- end}}
|
||||
This is your master admin account (and not a user account).
|
||||
To create a user account. Please follow steps below.
|
||||
|
||||
============================================
|
||||
Setup Email
|
||||
============================================
|
||||
|
||||
When you're done signing up the admin account. Head over to "Settings" -> "Email"
|
||||
|
||||
Add your SMTP server details here to enable email alerts.
|
||||
|
||||
============================================
|
||||
Setup Twilio
|
||||
============================================
|
||||
|
||||
When you're done signing up the admin account. Head over to "Settings" -> "Call and SMS"
|
||||
|
||||
Add your Twilio Settings here to enable call and SMS alert.
|
||||
|
||||
============================================
|
||||
Create User
|
||||
============================================
|
||||
|
||||
On the Admin Dahboard, go to the "Users" menu and add a new user.
|
||||
Log out of the admin account, and log in with a user account to access User's OneUptime Dashboard.
|
||||
|
||||
{{- if not $.Values.isThirdPartyBilling }}
|
||||
============================================
|
||||
STEP 5: Buy License
|
||||
============================================
|
||||
|
||||
OneUptime which you just installed runs on an evaluation license.
|
||||
Please contact us at sales@oneuptime.com to buy a commercial license.
|
||||
We support companies of all sizes.
|
||||
|
||||
Once you buy the commercial license,
|
||||
you can enter that license key on your admin dashboard.
|
||||
{{- end }}
|
||||
|
||||
============================================
|
||||
Support and Demo
|
||||
============================================
|
||||
|
||||
Demo:
|
||||
If you're looking for a personlized OneUptime demo.
|
||||
Please email us at demo@oneuptime.com to schedule one.
|
||||
|
||||
Support and Help:
|
||||
If you're looking for help with anything,
|
||||
Please email us at support@oneuptime.com and we'll get back to you in less than 1 business day.
|
||||
|
||||
|
||||
Thank you for installing OneUptime!
|
||||
44
HelmChart/public/oneuptime-deprecated/templates/_helpers.tpl
Normal file
44
HelmChart/public/oneuptime-deprecated/templates/_helpers.tpl
Normal file
@@ -0,0 +1,44 @@
|
||||
{{/* vim: set filetype=mustache: */}}
|
||||
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "oneuptime.mongodbConnectionString" -}}
|
||||
{{ printf "mongodb://%s:%s@%s-%s.%s-%s.%s.%s:%s/%s" $.Values.mongo.oneuptimeDbUsername $.Values.mongo.oneuptimeDbPassword $.Release.Name "mongo-standalone-0" $.Release.Name "mongo-standalone" $.Release.Namespace "svc.cluster.local" "27017" $.Values.mongo.databaseName }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "oneuptime.internalSmtpServer" -}}
|
||||
{{ printf "%s-haraka.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "oneuptime.redisHost" -}}
|
||||
{{ printf "%s-redis-master.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "oneuptime.backendHost" -}}
|
||||
{{ printf "%s-backend.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "oneuptime.oneuptimeHost" -}}
|
||||
{{ printf "%s-backend.%s" $.Values.oneuptime.host }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "oneuptime.serverUrl" -}}
|
||||
{{ printf "http://%s-backend.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "oneuptime.probeApiUrl" -}}
|
||||
{{ printf "http://%s-ProbeAPI.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "oneuptime.scriptRunnerUrl" -}}
|
||||
{{ printf "http://%s-script.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "oneuptime.dataIngestorUrl" -}}
|
||||
{{ printf "http://%s-ingestor.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "oneuptime.realtimeUrl" -}}
|
||||
{{ printf "http://%s-realtime.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
|
||||
{{- end -}}
|
||||
105
HelmChart/public/oneuptime-deprecated/templates/accounts.yaml
Normal file
105
HelmChart/public/oneuptime-deprecated/templates/accounts.yaml
Normal file
@@ -0,0 +1,105 @@
|
||||
############-----ACCOUNTS----#############################
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
app.kubernetes.io/part-of: oneuptime
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
replicas: {{ $.Values.replicaCount }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
spec:
|
||||
containers:
|
||||
- image: {{ printf "%s/%s/%s:%s" .Values.image.registry .Values.image.repository "accounts" .Values.image.tag }}
|
||||
name: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
imagePullPolicy: {{ $.Values.image.pullPolicy }}
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
limits:
|
||||
cpu: 500m
|
||||
env:
|
||||
{{- if .Values.saas.isSaasService }}
|
||||
- name: STRIPE_PUBLIC_KEY
|
||||
value: {{ $.Values.saas.stripe.publicKey }}
|
||||
- name: BILLING_ENABLED
|
||||
value: 'true'
|
||||
- name: AMPLITUDE_PUBLIC_KEY
|
||||
value: {{ $.Values.saas.amplitude.key }}
|
||||
{{- end }}
|
||||
- name: NODE_ENV
|
||||
value: {{ $.Values.nodeEnv }}
|
||||
- name: DISABLE_SIGNUP
|
||||
value: {{ $.Values.disableSignup | quote }}
|
||||
- name: NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
- name: POD_SERVICE_ACCOUNT
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.serviceAccountName
|
||||
ports:
|
||||
- containerPort: {{ $.Values.host.accountsPort }}
|
||||
hostPort: {{ $.Values.host.accountsPort }}
|
||||
name: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
restartPolicy: {{ $.Values.image.restartPolicy }}
|
||||
|
||||
---
|
||||
# OneUptime Accounts Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
app.kubernetes.io/part-of: oneuptime
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
name: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
spec:
|
||||
ports:
|
||||
- port: {{ $.Values.host.accountsServicePort }}
|
||||
targetPort: {{ $.Values.host.accountsPort }}
|
||||
selector:
|
||||
app: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
type: ClusterIP
|
||||
---
|
||||
###########################################
|
||||
|
||||
|
||||
{{- if .Values.autoScaler.enabled }}
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
spec:
|
||||
maxReplicas: {{ $.Values.autoScaler.maxReplicas }}
|
||||
minReplicas: {{ $.Values.autoScaler.minReplicas }}
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ printf "%s-%s" $.Release.Name "accounts" }}
|
||||
targetCPUUtilizationPercentage: {{ $.Values.autoScaler.averageCpuUtilization }}
|
||||
---
|
||||
{{- end }}
|
||||
@@ -0,0 +1,103 @@
|
||||
############-AdminDashboard-#########
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
app.kubernetes.io/part-of: oneuptime
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
replicas: {{ $.Values.replicaCount }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
spec:
|
||||
containers:
|
||||
- image: {{ printf "%s/%s/%s:%s" .Values.image.registry .Values.image.repository "AdminDashboard" .Values.image.tag }}
|
||||
name: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
imagePullPolicy: {{ $.Values.image.pullPolicy }}
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
limits:
|
||||
cpu: 500m
|
||||
env:
|
||||
- name: NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
- name: POD_SERVICE_ACCOUNT
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.serviceAccountName
|
||||
{{- if .Values.saas.isSaasService }}
|
||||
- name: BILLING_ENABLED
|
||||
value: 'true'
|
||||
{{- else }}
|
||||
- name: LICENSE_URL
|
||||
value: {{ $.Values.oneuptime.licensingUrl }}
|
||||
{{- end }}
|
||||
- name: NODE_ENV
|
||||
value: {{ $.Values.nodeEnv }}
|
||||
- name: IS_THIRD_PARTY_BILLING
|
||||
value: {{ $.Values.isThirdPartyBilling | quote }}
|
||||
- name: INTERNAL_SMTP_SERVER
|
||||
value: {{ template "oneuptime.internalSmtpServer" . }}
|
||||
ports:
|
||||
- containerPort: {{ $.Values.host.adminDashboardPort }}
|
||||
hostPort: {{ $.Values.host.adminDashboardPort }}
|
||||
name: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
restartPolicy: {{ $.Values.image.restartPolicy }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
app.kubernetes.io/part-of: oneuptime
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
name: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
spec:
|
||||
ports:
|
||||
- port: {{ $.Values.host.adminDashboardServicePort }}
|
||||
targetPort: {{ $.Values.host.adminDashboardPort }}
|
||||
selector:
|
||||
app: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
type: ClusterIP
|
||||
---
|
||||
##################################
|
||||
|
||||
{{- if .Values.autoScaler.enabled }}
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
spec:
|
||||
maxReplicas: {{ $.Values.autoScaler.maxReplicas }}
|
||||
minReplicas: {{ $.Values.autoScaler.minReplicas }}
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ printf "%s-%s" $.Release.Name "admin" }}
|
||||
targetCPUUtilizationPercentage: {{ $.Values.autoScaler.averageCpuUtilization }}
|
||||
---
|
||||
{{- end }}
|
||||
111
HelmChart/public/oneuptime-deprecated/templates/dashboard.yaml
Normal file
111
HelmChart/public/oneuptime-deprecated/templates/dashboard.yaml
Normal file
@@ -0,0 +1,111 @@
|
||||
############-----DASHBOARD----#############################
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
app.kubernetes.io/part-of: oneuptime
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
replicas: {{ $.Values.replicaCount }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
spec:
|
||||
containers:
|
||||
- image: {{ printf "%s/%s/%s:%s" .Values.image.registry .Values.image.repository "dashboard" .Values.image.tag }}
|
||||
name: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
imagePullPolicy: {{ $.Values.image.pullPolicy }}
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
limits:
|
||||
cpu: 500m
|
||||
env:
|
||||
- name: NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
- name: POD_SERVICE_ACCOUNT
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.serviceAccountName
|
||||
- name: DOMAIN
|
||||
value: {{ $.Values.domain }}
|
||||
- name: NODE_ENV
|
||||
value: {{ $.Values.nodeEnv }}
|
||||
- name: PUSHNOTIFICATION_PUBLIC_KEY
|
||||
value: {{ $.Values.pushNotification.publicKey }}
|
||||
{{- if .Values.statusPageDomain }}
|
||||
- name: STATUSPAGE_DOMAIN
|
||||
value: {{ $.Values.statusPageDomain }}
|
||||
{{- end }}
|
||||
{{- if .Values.saas.isSaasService }}
|
||||
- name: STRIPE_PUBLIC_KEY
|
||||
value: {{ $.Values.saas.stripe.publicKey }}
|
||||
- name: BILLING_ENABLED
|
||||
value: 'true'
|
||||
- name: AMPLITUDE_PUBLIC_KEY
|
||||
value: {{ $.Values.saas.amplitude.key }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- containerPort: {{ $.Values.host.dashboardPort }}
|
||||
hostPort: {{ $.Values.host.dashboardPort }}
|
||||
name: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
restartPolicy: {{ $.Values.image.restartPolicy }}
|
||||
|
||||
---
|
||||
# OneUptime Dashoard Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
app.kubernetes.io/part-of: oneuptime
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
name: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
spec:
|
||||
ports:
|
||||
- port: {{ $.Values.host.dashboardServicePort }}
|
||||
targetPort: {{ $.Values.host.dashboardPort }}
|
||||
selector:
|
||||
app: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
type: ClusterIP
|
||||
---
|
||||
##########################################################
|
||||
|
||||
|
||||
{{- if .Values.autoScaler.enabled }}
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
spec:
|
||||
maxReplicas: {{ $.Values.autoScaler.maxReplicas }}
|
||||
minReplicas: {{ $.Values.autoScaler.minReplicas }}
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ printf "%s-%s" $.Release.Name "dashboard" }}
|
||||
targetCPUUtilizationPercentage: {{ $.Values.autoScaler.averageCpuUtilization }}
|
||||
---
|
||||
{{- end }}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user