Compare commits

...

70 Commits

Author SHA1 Message Date
Simon Larsen
dfa85982c2 fix fmt 2023-09-22 09:41:24 +01:00
Simon Larsen
8451f9f90d fix api docs url 2023-09-22 09:19:39 +01:00
Simon Larsen
c447941755 fix tests 2023-09-22 09:15:43 +01:00
Simon Larsen
112e2e4faa add 1 second between retries 2023-09-22 08:15:45 +01:00
Simon Larsen
fed9ab4621 fix database connect 2023-09-21 20:58:20 +01:00
Simon Larsen
18461d58d6 add comment to LIMIT_MAX 2023-09-21 20:34:48 +01:00
Simon Larsen
11095fc0bc add version 2023-09-21 20:31:00 +01:00
Simon Larsen
a567cee47f fix lint 2023-09-21 18:15:21 +01:00
Simon Larsen
158e2abc12 add app version 2023-09-21 18:01:40 +01:00
Simon Larsen
ca2095e867 refresh next function 2023-09-21 17:16:34 +01:00
Simon Larsen
708a9ba4b8 increase npm timeout in docker files 2023-09-21 17:09:09 +01:00
Simon Larsen
91a60eabbb fix fmt 2023-09-21 17:07:55 +01:00
Simon Larsen
7f0e07bd40 implement retry if the server is 500 status 2023-09-21 17:02:50 +01:00
Simon Larsen
01b17b9dff Merge branch 'master' of github.com-simon:OneUptime/oneuptime 2023-09-21 13:06:37 +01:00
Simon Larsen
b7e5cf78b9 fix fmt 2023-09-21 13:06:06 +01:00
Simon Larsen
d92d8c260b add retry 2023-09-21 12:53:20 +01:00
Simon Larsen
0576199db8 add exception 2023-09-21 12:52:04 +01:00
Simon Larsen
6f5a804d77 refactor retry 2023-09-21 12:48:03 +01:00
Simon Larsen
2f6420152e add retry if there's packet loss 2023-09-21 12:39:36 +01:00
Simon Larsen
1d1d11bee2 Merge pull request #770 from OneUptime/helm-chart
Helm chart
2023-09-21 11:00:35 +01:00
Simon Larsen
594c36a512 fix common server test 2023-09-21 09:51:25 +01:00
Simon Larsen
ec5c852175 deprecate old charts and add new chart 2023-09-20 21:23:52 +01:00
Simon Larsen
01ff01029b fix fmt 2023-09-20 18:51:19 +01:00
Simon Larsen
bf5d16b64c fix fmt 2023-09-20 18:37:43 +01:00
Simon Larsen
26ee469467 change p to div to not apply p styles 2023-09-20 18:21:59 +01:00
Simon Larsen
b514c8c189 fix user details in master admin 2023-09-20 18:20:20 +01:00
Simon Larsen
2bbac9a545 fix api key perms 2023-09-20 18:00:30 +01:00
Simon Larsen
03386eeba0 add permissions to see the model. 2023-09-20 16:08:17 +01:00
Simon Larsen
a09842c8b0 add master key request perms 2023-09-20 15:38:07 +01:00
Simon Larsen
847b75b555 add api key ui to admin dash 2023-09-20 15:22:18 +01:00
Simon Larsen
c839317283 remove dup file 2023-09-20 11:08:32 +01:00
Simon Larsen
1f4fd86845 fix fmt 2023-09-20 10:58:20 +01:00
Simon Larsen
8ed8c6a05c add config.example 2023-09-20 09:57:13 +01:00
Simon Larsen
a716d54cc6 remove host settings from admin dash 2023-09-20 09:51:32 +01:00
Simon Larsen
668d00418d fix issue with has invitation accepted 2023-09-20 07:52:45 +01:00
Simon Larsen
5c36fa851c remove token secret 2023-09-14 14:14:27 +05:30
Simon Larsen
4352ada83e remove unused vars 2023-09-14 13:55:17 +05:30
Simon Larsen
c6d76c4bb0 remove unused vars 2023-09-14 13:53:31 +05:30
Simon Larsen
87a1a84d2e remove unused vars 2023-09-14 13:53:07 +05:30
Simon Larsen
b02e622fd3 fix hostname in config.env 2023-09-14 11:47:56 +05:30
Simon Larsen
ac7e6b915f remove hostname and route from config.env 2023-09-14 11:46:49 +05:30
Simon Larsen
f3e1dccfc1 fix fmt on env config 2023-09-14 10:15:02 +05:30
Simon Larsen
8683ac7677 add admin dash status check 2023-09-14 10:11:37 +05:30
Simon Larsen
eccb65f930 remove env vars from env config. 2023-09-14 09:58:45 +05:30
Simon Larsen
c1c27a387c fix reseller plan id 2023-09-13 23:23:57 +05:30
Simon Larsen
aece287747 fix docker files 2023-09-13 22:30:39 +05:30
Simon Larsen
d6ff2c12fb fix docker files 2023-09-13 21:40:25 +05:30
Simon Larsen
50c8fe003d fix fmt 2023-09-13 21:17:32 +05:30
Simon Larsen
3e6b16fcf6 add smtp in admin dash 2023-09-13 21:04:54 +05:30
Simon Larsen
ce43514ee3 add sendgrid api key 2023-09-13 20:28:53 +05:30
Simon Larsen
8318f09e26 fix mail server 2023-09-13 20:18:46 +05:30
Simon Larsen
7711902edd add email server type 2023-09-13 20:14:42 +05:30
Simon Larsen
94ffa754eb add use internal smtp server 2023-09-13 19:20:16 +05:30
Simon Larsen
48035ddec0 add plan type to model table 2023-09-13 16:30:52 +05:30
Simon Larsen
7694abe05e fix accounts webpack config 2023-09-13 16:11:31 +05:30
Simon Larsen
8eac47c4f9 fix typo nothing 2023-09-13 15:53:30 +05:30
Simon Larsen
fe90b50862 fix global config api 2023-09-13 15:53:04 +05:30
Simon Larsen
e3f2eaa3c6 fix fmt 2023-09-13 15:33:41 +05:30
Simon Larsen
fc09c689bc fix fmt 2023-09-13 15:13:12 +05:30
Simon Larsen
faf04a726c fix compile in common ui 2023-09-13 14:49:44 +05:30
Simon Larsen
31e04a26ff fix docker files 2023-09-13 14:28:57 +05:30
Simon Larsen
90ea8ebee9 remove pptr 2023-09-13 14:11:48 +05:30
Simon Larsen
4d2e66fce3 fix base 2023-09-13 13:56:36 +05:30
Simon Larsen
6057fafd97 fix docker files 2023-09-13 13:55:54 +05:30
Simon Larsen
57671c444c refactor docker files 2023-09-13 13:53:26 +05:30
Simon Larsen
11a3111098 fix compile err in identity 2023-09-13 13:40:10 +05:30
Simon Larsen
e74c711dfd fix compie err in workers 2023-09-13 13:38:50 +05:30
Simon Larsen
4f64693550 remove axios from webpack 2023-09-13 13:37:37 +05:30
Simon Larsen
2336961178 Merge branch 'master' of github.com-simon:OneUptime/oneuptime 2023-09-13 13:35:19 +05:30
Simon Larsen
c7938f62ae Merge pull request #746 from OneUptime/admin-dashboard
Admin dashboard
2023-09-13 13:10:45 +05:30
144 changed files with 3589 additions and 1536 deletions

View File

@@ -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

View File

@@ -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',
}

View File

@@ -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

View File

@@ -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>
);

View 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;

View File

@@ -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,

View File

@@ -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>
);
};

View File

@@ -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>
);
};

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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

View File

@@ -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,

View File

@@ -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) => {

View File

@@ -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
}
}
},

View File

@@ -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",

View File

@@ -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

View File

@@ -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;

View File

@@ -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');

View File

@@ -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;

View File

@@ -7,6 +7,7 @@ enum ExceptionCode {
WebRequestException = 6,
BadDataException = 400,
BadRequestException = 400,
UnabletoReachServerException = 415,
ServerException = 500,
NotAuthorizedException = 403,
NotAuthenticatedException = 401,

View 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);
}
}

View File

@@ -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;
};
};

View File

@@ -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(

View File

@@ -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);
}
}
);
}
}

View File

@@ -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);
}

View File

@@ -45,6 +45,7 @@ export default class ProjectAPI extends BaseAPI<Project, ProjectServiceType> {
query: {
userId: (req as OneUptimeRequest)
.userAuthorization!.userId!,
hasAcceptedInvitation: true,
},
select: {
project: {

View File

@@ -205,6 +205,7 @@ export default class ResellerPlanAPI extends BaseAPI<
data: {
activeMonitorsLimit: resellerPlan.monitorLimit!,
seatLimit: resellerPlan.teamMemberLimit!,
resellerPlanId: resellerPlan.id!,
},
props: {
isRoot: true,

View File

@@ -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,
},

View File

@@ -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')
);
}

View File

@@ -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';

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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')
);
});

View File

@@ -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>,

View File

@@ -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') {

View File

@@ -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);

View File

@@ -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",

View File

@@ -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",

View File

@@ -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

View File

@@ -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>
);
};

View 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;

View File

@@ -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 =

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -0,0 +1 @@
.git

View 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

View 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!

View 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 -}}

View File

@@ -0,0 +1,126 @@
############----INGRESS---#####################################
{{- range $key, $value := $.Values.oneuptime.hosts }}
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
labels:
app.kubernetes.io/part-of: oneuptime
app.kubernetes.io/managed-by: Helm
meta.helm.sh/release-name: {{ printf "%s" $.Release.Name }}
annotations:
kubernetes.io/ingress.class: nginx
ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/affinity-mode: "balanced"
nginx.ingress.kubernetes.io/session-cookie-change-on-failure: "false"
nginx.ingress.kubernetes.io/session-cookie-name: "fi-ingress-cookie"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
ingress.kubernetes.io/session-cookie-hash: "sha1"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
name: {{ printf "%s-%s-%s" $.Release.Name $key "ingress" }}
namespace: {{ $.Release.Namespace }}
spec:
{{- if $value.tls.enabled }}
tls:
- hosts:
- {{ $value.host }}
secretName: {{ printf "%s-%s-%s" $.Release.Name $key "tls" }}
{{- end }}
rules:
{{- if $value.host }}
- host: {{ $value.host }}
http:
{{- else }}
- http:
{{- end }}
paths:
- path: /accounts
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "accounts" }}
servicePort: 80
- path: /data-ingestor
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "ingestor" }}
servicePort: 80
- path: /probe-api
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "probe-api" }}
servicePort: 80
- path: /dashboard
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "dashboard" }}
servicePort: 80
- path: /admin
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "admin" }}
servicePort: 80
- path: /api
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "backend" }}
servicePort: 80
- path: /realtime
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "realtime" }}
servicePort: 80
- path: /StatusPage
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "status" }}
servicePort: 80
- path: /haraka
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "haraka" }}
servicePort: 80
- path: /application
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "app-scan" }}
servicePort: 80
- path: /container
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "cont-scan" }}
servicePort: 80
- path: /lighthouse
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "lighthouse" }}
servicePort: 80
- path: /script
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "script" }}
servicePort: 80
{{- range $probeKey, $probeValue := $.Values.probes }}
- path: {{ printf "/%s" $probeKey }}
backend:
serviceName: {{ printf "%s-%s" $.Release.Name $probeKey }}
servicePort: 80
{{- end }}
{{- if $.Values.saas.isSaasService }}
- path: /
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "home" }}
servicePort: 80
- path: /docs
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "api-reference" }}
servicePort: 80
- path: /license
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "licensing" }}
servicePort: 80
- path: /chart
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "helm-chart" }}
servicePort: 80
{{- else }}
- path: /
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "accounts" }}
servicePort: 80
{{- end }}
{{- if $.Values.saas.exposeInitScriptContainer }}
- path: /
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "init-script" }}
servicePort: 80
{{- end }}
---
{{- end }}
##########################################################################

View File

@@ -0,0 +1,258 @@
##################################################################################
## Important: If you're implenting this in the enterprise environment, this should always be `false`.
## This is for OneUptime SaaS service. This will deploy all the SaaS env vars
##################################################################################
saas:
exposeInitScriptContainer: false
isSaasService: false
stripe:
publicKey: #Stripe public and private key
privateKey:
slackWebhook:
airtable:
key: #Airtbale key to store leads.
baseId:
amplitude:
key: #Amplitude for tracking.
twitter:
bearertoken: # Twitter Bearer Token.
licensing:
airtable: # Airtable for validating licenses.
key:
baseId:
tokenSecret: # Encrypting lisense with tokens to send them to clients.
###################################################################################
statusPage:
hosts:
host1:
host:
tls:
enabled: false
crt:
key:
cert: # certificate for a custom domain
key: # private key for a custom domain
##################################################################################
## Important: OneUptime Values. More information in the Readme.md
##################################################################################
oneuptime:
admin:
email:
password:
hosts:
host1:
host:
tls:
enabled: false
crt:
key:
licensingUrl: https://oneuptime.com/license
###################################################################################
httpTestServer:
hosts:
host1:
host:
tls:
enabled: false
crt:
key:
##################################################################################
## Important: Probe Values. More information in the Readme.md
##################################################################################
probes:
probe1:
name: Probe 1
key: sample-key
probe2:
name: Probe 2
key: sample-key
##################################################################################
## Important: RateLimitter Values. More information in the Readme.md
##################################################################################
rateLimitter:
enabled: false
requestLimit: 5000
requestLimitTimePeriodInMS: 216000
## OneUptime official image version on Docker Hub
## ref: https://hub.docker.com/u/oneuptime
##
image:
registry: docker.io
repository: oneuptime
tag: latest
pullPolicy: Always
restartPolicy: Always
replicaCount: 1
##################################################################################
## IMPORTANT:
## Values for Subcharts
##
redis:
redisPort: 6379
image:
registry: docker.io
repository: bitnami/redis
tag: latest
pullPolicy: Always
usePassword: false
auth:
enabled: false
persistence:
enabled: true
mountPath: /bitnami/redis
size: 20Gi
mongo:
oneuptimeDbUsername: oneuptime
oneuptimeDbPassword: password
databaseName: oneuptimedb
rootUsername: root
rootPassword: root
## Service Configuration
## For minikube, set service.type to NodePort, elsewhere use LoadBalancer
##
## Service Configuration
## For minikube, set this to NodePort, elsewhere use LoadBalancer
##
nginx-ingress-controller:
defaultBackend:
enabled: false
service:
type: LoadBalancer
proxySetHeaders:
X-Forwarded-For: $http_x_forwarded_for
X-Real-Ip: $http_x_forwarded_for
publishService:
enabled: true
config:
log-format-upstream: '$remote_addr - $http_cf_connecting_ip - $http_x_forwarded_for - $request_id - [$proxy_add_x_forwarded_for] - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status'
proxy-protocol: "true"
real-ip-header: "X-Forwarded-For"
##################################################################################
haraka:
fromEmail:
fromName:
user:
password:
domain:
# This should be base64 of your private key
dkimPrivateKey:
tlsCert:
tlsKey:
host:
backendPort: 3002
fetchResourcesPort: 3400
dataIngestorPort: 3200
realtimePort: 3300
homePort: 1444
licensingPort: 3004
statusPageHttpPort: 3006
statusPageHttpsPort: 3007
dashboardPort: 3000
accountsPort: 3003
helmChartPort: 3423
ApiReferencePort: 1445
initScriptPort: 1447
# if port 25 is avaialble and accessible
# then we can use it as default port
harakaPort: 2525
httpTestServerPort: 3010
adminDashboardPort: 3100
backendServicePort: 80
dataIngestorServicePort: 80
realtimeServicePort: 80
fetchResourcesServicePort: 80
homeServicePort: 80
ApiReferenceServicePort: 80
statusPageHttpServicePort: 80
statusPageHttpsServicePort: 443
dashboardServicePort: 80
accountsServicePort: 80
adminDashboardServicePort: 80
backendNodePort: 80
statusPageNodePort: 80
dashboardNodePort: 80
accountsNodePort: 80
adminDashboardNodePort: 80
licensingServicePort: 80
helmChartServicePort: 80
httpTestServerServicePort: 80
initScriptServicePort: 80
harakaServicePort: 2525
scriptRunnerPort: 3009
scripRunnerServicePort: 80
applicationScannerPort: 3005
applicationScannerServicePort: 80
containerScannerPort: 3055
containerScannerServicePort: 80
lighthouseRunnerPort: 3015
lighthouseRunnerServicePort: 80
isThirdPartyBilling: false
isRunningOnGCPMarketplace: false
isTesting: false
# Encryption Key
encryptionKey: ThisEncryptionKeyLengthIs32Size.
# If you disable sign up, then you need to specify
# oneuptime.admin.email and oneuptime.admin.password values for the admin account.
disableSignup: false
# What protocol is backend running on. Takes in values like 'http:' or 'https:'
backendProtocol:
# Status page domain to add to CNAME to work with custom domains
# Status page CNAME to display whcih customers will set on the cusom domain tab of status page.
statusPageDomain:
# Push Notifications.
# Generate public and pivate key for push notifications.
# These are just the test keys.
# You can generate them by:
# First install the web-push npm package using npm install web-push -g, then run web-push generate-vapid-keys
pushNotification:
publicKey: "BD1kb-OchZlXr32bmwpjhoxp_cq-aqK4dWXRDkC5m6Hd9_cvMOUw_bXRFR3pJFGzpEdjQUk5SDdYaXvb7xd-1Dg"
privateKey: "WdFZTeXkuoxpsO_KNOtXvhDUc_Ae1rb-WjPv6AVexA4"
url: "https://oneuptime-test.com"
autoScaler:
enabled: false
averageCpuUtilization: 50
minReplicas: 1
maxReplicas: 1
nodeEnv: production
applicationScanner:
name: US
key: b6ETg5EdrIEtdjgit7r3
containerScanner:
name: US
key: s9KOOlCVyly0XykZVjDe
logstashHost: 54.153.39.163
logstashPort: 1514

View File

@@ -1 +1,23 @@
.git
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@@ -1,33 +1,24 @@
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
name: oneuptime
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"

View File

@@ -1,85 +1,22 @@
============================================
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.
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "oneuptime.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "oneuptime.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "oneuptime.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "oneuptime.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- 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!

View File

@@ -1,44 +1,62 @@
{{/* 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.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "oneuptime.internalSmtpServer" -}}
{{ printf "%s-haraka.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "oneuptime.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{- define "oneuptime.redisHost" -}}
{{ printf "%s-redis-master.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "oneuptime.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "oneuptime.backendHost" -}}
{{ printf "%s-backend.%s.%s" $.Release.Name $.Release.Namespace "svc.cluster.local" }}
{{- end -}}
{{/*
Common labels
*/}}
{{- define "oneuptime.labels" -}}
helm.sh/chart: {{ include "oneuptime.chart" . }}
{{ include "oneuptime.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "oneuptime.oneuptimeHost" -}}
{{ printf "%s-backend.%s" $.Values.oneuptime.host }}
{{- end -}}
{{/*
Selector labels
*/}}
{{- define "oneuptime.selectorLabels" -}}
app.kubernetes.io/name: {{ include "oneuptime.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- 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 -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "oneuptime.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "oneuptime.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,61 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "oneuptime.fullname" . }}
labels:
{{- include "oneuptime.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "oneuptime.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "oneuptime.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "oneuptime.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,28 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "oneuptime.fullname" . }}
labels:
{{- include "oneuptime.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "oneuptime.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View File

@@ -1,126 +1,61 @@
############----INGRESS---#####################################
{{- range $key, $value := $.Values.oneuptime.hosts }}
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "oneuptime.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app.kubernetes.io/part-of: oneuptime
app.kubernetes.io/managed-by: Helm
meta.helm.sh/release-name: {{ printf "%s" $.Release.Name }}
{{- include "oneuptime.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
kubernetes.io/ingress.class: nginx
ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/affinity-mode: "balanced"
nginx.ingress.kubernetes.io/session-cookie-change-on-failure: "false"
nginx.ingress.kubernetes.io/session-cookie-name: "fi-ingress-cookie"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
ingress.kubernetes.io/session-cookie-hash: "sha1"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
name: {{ printf "%s-%s-%s" $.Release.Name $key "ingress" }}
namespace: {{ $.Release.Namespace }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if $value.tls.enabled }}
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
- {{ $value.host }}
secretName: {{ printf "%s-%s-%s" $.Release.Name $key "tls" }}
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- if $value.host }}
- host: {{ $value.host }}
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
{{- else }}
- http:
{{- end }}
paths:
- path: /accounts
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "accounts" }}
servicePort: 80
- path: /data-ingestor
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "ingestor" }}
servicePort: 80
- path: /probe-api
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "probe-api" }}
servicePort: 80
- path: /dashboard
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "dashboard" }}
servicePort: 80
- path: /admin
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "admin" }}
servicePort: 80
- path: /api
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "backend" }}
servicePort: 80
- path: /realtime
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "realtime" }}
servicePort: 80
- path: /StatusPage
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "status" }}
servicePort: 80
- path: /haraka
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "haraka" }}
servicePort: 80
- path: /application
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "app-scan" }}
servicePort: 80
- path: /container
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "cont-scan" }}
servicePort: 80
- path: /lighthouse
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "lighthouse" }}
servicePort: 80
- path: /script
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "script" }}
servicePort: 80
{{- range $probeKey, $probeValue := $.Values.probes }}
- path: {{ printf "/%s" $probeKey }}
backend:
serviceName: {{ printf "%s-%s" $.Release.Name $probeKey }}
servicePort: 80
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- if $.Values.saas.isSaasService }}
- path: /
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "home" }}
servicePort: 80
- path: /docs
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "api-reference" }}
servicePort: 80
- path: /license
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "licensing" }}
servicePort: 80
- path: /chart
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "helm-chart" }}
servicePort: 80
{{- else }}
- path: /
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "accounts" }}
servicePort: 80
{{- end }}
{{- if $.Values.saas.exposeInitScriptContainer }}
- path: /
backend:
serviceName: {{ printf "%s-%s" $.Release.Name "init-script" }}
servicePort: 80
{{- end }}
---
{{- end }}
{{- end }}
##########################################################################

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "oneuptime.fullname" . }}
labels:
{{- include "oneuptime.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "oneuptime.selectorLabels" . | nindent 4 }}

Some files were not shown because too many files have changed in this diff Show More