mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
Merge branch 'master' of https://gitlab.com/fyipe-project/app into feature-ruby-sdk
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,7 +17,6 @@ node_modules
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
env.js
|
||||
/.vscode
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
|
||||
@@ -120,8 +120,6 @@ include:
|
||||
- '/ci/spec/ruby-sdk/build-n-test.yaml'
|
||||
- '/ci/spec/ruby-sdk/deploy.yaml'
|
||||
|
||||
|
||||
|
||||
# # HARAKA
|
||||
# - '/ci/spec/haraka/deploy.yaml'
|
||||
|
||||
@@ -133,3 +131,9 @@ include:
|
||||
|
||||
# # FYIPE-GL-MANAGER
|
||||
# - '/ci/spec/fyipe-gl-manager/deploy.yaml'
|
||||
|
||||
# # Application Scanner
|
||||
# - '/ci/spec/application-scanner/deploy.yaml'
|
||||
|
||||
# # Script Runner
|
||||
# - '/ci/spec/script-runner/deploy.yaml'
|
||||
92
.vscode/launch.json
vendored
Normal file
92
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"localRoot": "${workspaceFolder}/backend",
|
||||
"name": "Backend: Debug with Docker",
|
||||
"port": 9232,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "pwa-node"
|
||||
},
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"localRoot": "${workspaceFolder}/licensing",
|
||||
"name": "Licensing: Debug with Docker",
|
||||
"port": 9233,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "pwa-node"
|
||||
},
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"localRoot": "${workspaceFolder}/http-test-server",
|
||||
"name": "HTTP Test Server: Debug with Docker",
|
||||
"port": 9234,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "pwa-node"
|
||||
},
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"localRoot": "${workspaceFolder}/home",
|
||||
"name": "Home: Debug with Docker",
|
||||
"port": 9235,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "pwa-node"
|
||||
},
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"localRoot": "${workspaceFolder}/script-runnner",
|
||||
"name": "Script Runner: Debug with Docker",
|
||||
"port": 9236,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "pwa-node"
|
||||
},
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"localRoot": "${workspaceFolder}/init-script",
|
||||
"name": "Init Script: Debug with Docker",
|
||||
"port": 9237,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "pwa-node"
|
||||
},
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"localRoot": "${workspaceFolder}/probe",
|
||||
"name": "Probe: Debug with Docker",
|
||||
"port": 9238,
|
||||
"remoteRoot": "/usr/src/app",
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "pwa-node"
|
||||
}
|
||||
]
|
||||
}
|
||||
1541
_test/script-monitor/package-lock.json
generated
Normal file
1541
_test/script-monitor/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
_test/script-monitor/package.json
Normal file
21
_test/script-monitor/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "script-monitor",
|
||||
"version": "1.0.0",
|
||||
"description": "Test for script monitor",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha ."
|
||||
},
|
||||
"author": "Dave",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"chai": "^4.3.4",
|
||||
"mocha": "^8.4.0",
|
||||
"request": "^2.88.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"express": "^4.17.1",
|
||||
"vm2": "^3.9.2"
|
||||
}
|
||||
}
|
||||
215
_test/script-monitor/scriptSandbox.js
Normal file
215
_test/script-monitor/scriptSandbox.js
Normal file
@@ -0,0 +1,215 @@
|
||||
const { join } = require("path");
|
||||
const {performance} = require('perf_hooks');
|
||||
|
||||
// TODO - make this configurable from admin-dashboard
|
||||
const runConfig = {
|
||||
availableImports: ['axios'], // init allowed modules
|
||||
maxSyncStatementDuration: 3000,
|
||||
maxScriptRunTime: 5000,
|
||||
};
|
||||
|
||||
class ScriptMonitorError extends Error {
|
||||
constructor(errors, message = "Script monitor resource error") {
|
||||
super();
|
||||
this.message = message;
|
||||
this.errors = Array.isArray(errors)
|
||||
? errors.reduce(
|
||||
(allErr, err) => [...allErr, err.message].join(','),
|
||||
[]
|
||||
)
|
||||
: (errors.message ?? errors);
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
availableImports,
|
||||
maxScriptRunTime,
|
||||
maxSyncStatementDuration,
|
||||
} = runConfig;
|
||||
|
||||
const runScript = async (functionCode, isCalled, options = { maxScriptRunTime, maxSyncStatementDuration }) => {
|
||||
const {
|
||||
isMainThread,
|
||||
Worker,
|
||||
parentPort,
|
||||
workerData,
|
||||
} = require('worker_threads');
|
||||
|
||||
if (isMainThread) {
|
||||
// modifiable option in development mode only
|
||||
const { maxScriptRunTime, maxSyncStatementDuration } = options;
|
||||
if (!isCalled) return;
|
||||
const start = performance.now();
|
||||
return new Promise(resolve => {
|
||||
const worker = new Worker(__filename, {
|
||||
workerData: { functionCode },
|
||||
execArgv: [
|
||||
...process.execArgv,
|
||||
'--unhandled-rejections=strict',
|
||||
], // handle promise rejection warnings
|
||||
});
|
||||
|
||||
const consoleLogs = [];
|
||||
let lastMessage = null;
|
||||
|
||||
worker.on('message', ({type, payload}) => {
|
||||
switch (type) {
|
||||
case 'ping': {
|
||||
lastMessage = Date.now();
|
||||
break;
|
||||
}
|
||||
case 'log':{
|
||||
consoleLogs.push(payload);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (type.error) {
|
||||
resolve({
|
||||
success: false,
|
||||
error: type.error,
|
||||
status: 'error',
|
||||
executionTime: performance.now() - start,
|
||||
consoleLogs,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
worker.on('online', () => {
|
||||
lastMessage = Date.now();
|
||||
});
|
||||
worker.on('exit', exitCode => {
|
||||
switch (exitCode) {
|
||||
case 0:
|
||||
resolve({
|
||||
success: true,
|
||||
status: 'completed',
|
||||
executionTime: performance.now() - start,
|
||||
consoleLogs,
|
||||
});
|
||||
break;
|
||||
case 1: {
|
||||
const message = statementTimeExceeded
|
||||
? `Max. synchronous statement execution time exceeded (${maxSyncStatementDuration}ms)`
|
||||
: scriptTimeExceeded
|
||||
? `Max. script execution time exceeded (${maxScriptRunTime}ms)`
|
||||
: 'Script was terminated';
|
||||
resolve({
|
||||
success: false,
|
||||
message,
|
||||
status: 'timeout',
|
||||
executionTime: performance.now() - start,
|
||||
consoleLogs,
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
resolve({
|
||||
success: false,
|
||||
message: 'Unknown Error: script terminated',
|
||||
status: 'terminated',
|
||||
executionTime: performance.now() - start,
|
||||
consoleLogs,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
clearInterval(checker);
|
||||
});
|
||||
worker.on('error', err => {
|
||||
if (err.errors) {
|
||||
resolve({
|
||||
success: false,
|
||||
message: err.message,
|
||||
errors: err.errors,
|
||||
status: 'nonEmptyCallback',
|
||||
executionTime: performance.now() - start,
|
||||
consoleLogs,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
resolve({
|
||||
success: false,
|
||||
message: err.message,
|
||||
status: 'error',
|
||||
executionTime: performance.now() - start,
|
||||
consoleLogs,
|
||||
});
|
||||
clearInterval(checker);
|
||||
worker.terminate();
|
||||
});
|
||||
|
||||
let totalRuntime = 0,
|
||||
statementTimeExceeded = false,
|
||||
scriptTimeExceeded = false;
|
||||
|
||||
const checker = setInterval(
|
||||
() => {
|
||||
totalRuntime += 1000;
|
||||
if (totalRuntime > maxScriptRunTime) {
|
||||
clearInterval(checker);
|
||||
scriptTimeExceeded = true;
|
||||
worker.terminate();
|
||||
}
|
||||
// Last ping was too long ago, terminate it
|
||||
if (
|
||||
lastMessage !== null &&
|
||||
Date.now() - lastMessage >= maxSyncStatementDuration
|
||||
) {
|
||||
clearInterval(checker);
|
||||
statementTimeExceeded = true;
|
||||
worker.terminate();
|
||||
}
|
||||
},
|
||||
1000,
|
||||
maxSyncStatementDuration
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// worker_threads code
|
||||
const { NodeVM } = require('vm2');
|
||||
const vm = new NodeVM({
|
||||
eval: false,
|
||||
wasm: false,
|
||||
require: {
|
||||
root: './',
|
||||
external: availableImports,
|
||||
import: availableImports,
|
||||
},
|
||||
console: 'redirect',
|
||||
});
|
||||
|
||||
vm.on('console.log', (log) => {
|
||||
parentPort.postMessage({type: 'log', payload: `[log]: ${log}`});
|
||||
});
|
||||
|
||||
vm.on('console.error', (error) => {
|
||||
parentPort.postMessage({type: 'log', payload: `[error]: ${error}`});
|
||||
});
|
||||
|
||||
vm.on('console.warn', (error) => {
|
||||
parentPort.postMessage({type: 'log', payload: `[warn]: ${error}`});
|
||||
});
|
||||
|
||||
const scriptCompletedCallback = err => {
|
||||
if (err) {
|
||||
throw new ScriptMonitorError(err);
|
||||
}
|
||||
};
|
||||
|
||||
const code = workerData.functionCode;
|
||||
setInterval(() => parentPort.postMessage({type: 'ping'}), 500);
|
||||
const sandboxFunction = await vm.run(
|
||||
`module.exports = ${code}`,
|
||||
join(process.cwd(), 'node_modules')
|
||||
);
|
||||
|
||||
await sandboxFunction(scriptCompletedCallback);
|
||||
process.exit();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = runScript();
|
||||
module.exports.runScript = runScript;
|
||||
117
_test/script-monitor/scriptSandbox.test.js
Normal file
117
_test/script-monitor/scriptSandbox.test.js
Normal file
@@ -0,0 +1,117 @@
|
||||
const { expect } = require("chai");
|
||||
const { runScript } = require("./scriptSandbox");
|
||||
|
||||
describe('ScriptMonitor V2', function() {
|
||||
this.timeout(10000);
|
||||
describe("runScript function", function(){
|
||||
let server;
|
||||
|
||||
// create a quick express server
|
||||
before(function(){
|
||||
const express = require("express");
|
||||
const app = express();
|
||||
app.get("/test", (req, res) => res.send("yipee!"));
|
||||
server = app.listen(5050);
|
||||
});
|
||||
|
||||
// close express server
|
||||
after(function(){
|
||||
server.close();
|
||||
});
|
||||
|
||||
it("should return success for a valid script", async function() {
|
||||
const someFunction = async (done) => {
|
||||
// make api requests using "axios" or "request"
|
||||
const axios = require("axios").default;
|
||||
// const request = require("request-promise");
|
||||
|
||||
const res = await axios.get("http://localhost:5050/test");
|
||||
// const res = await request.get("http://localhost:5050/test");
|
||||
console.log("hello");
|
||||
console.log("world!");
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
const result = await runScript(someFunction.toString(), true);
|
||||
expect(result).to.not.be.undefined;
|
||||
expect(result.success).to.be.true;
|
||||
expect(result.status).eq("completed");
|
||||
expect(result.executionTime).to.be.a('number');
|
||||
console.log(result.executionTime);
|
||||
|
||||
expect(result.consoleLogs.length).eql(2);
|
||||
expect(result.consoleLogs).to.include('[log]: hello');
|
||||
expect(result.consoleLogs).to.include('[log]: world!');
|
||||
|
||||
});
|
||||
|
||||
it("should return false for error thrown in script", async function() {
|
||||
const someFunction = async (done) => {
|
||||
console.log('Error log');
|
||||
console.error('Bad Error');
|
||||
throw new Error("Bad error");
|
||||
}
|
||||
const result = await runScript(someFunction.toString(), true);
|
||||
|
||||
expect(result).to.not.be.undefined;
|
||||
expect(result.success).to.be.false;
|
||||
expect(result.status).eq("error");
|
||||
expect(result.executionTime).to.be.a('number');
|
||||
|
||||
console.log(result.executionTime);
|
||||
|
||||
expect(result.consoleLogs.length).eql(2);
|
||||
expect(result.consoleLogs).to.include('[error]: Bad Error');
|
||||
expect(result.consoleLogs).to.include('[log]: Error log');
|
||||
});
|
||||
|
||||
it("should return scriptMonitor error when script returns a value in cb", async function() {
|
||||
const someFunction = async (done) => {
|
||||
done("Some Error");
|
||||
}
|
||||
const result = await runScript(someFunction.toString(), true);
|
||||
|
||||
expect(result).to.be.ok;
|
||||
expect(result.success).to.be.false;
|
||||
expect(result.message).to.be.string("Script monitor resource error");
|
||||
expect(result.errors).to.be.ok;
|
||||
expect(result.status).eq("nonEmptyCallback");
|
||||
expect(result.executionTime).to.be.a('number');
|
||||
console.log(result.executionTime);
|
||||
});
|
||||
|
||||
it("should return timeout error when script takes too long", async function() {
|
||||
const someFunction = async (done) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => "All timed out", 7000);
|
||||
})
|
||||
}
|
||||
const result = await runScript(someFunction.toString(), true, {maxScriptRunTime: 1500});
|
||||
|
||||
expect(result).to.be.ok;
|
||||
expect(result.success).to.be.false;
|
||||
expect(result.message).contains("Max. script execution time exceeded");
|
||||
expect(result.status).eq("timeout");
|
||||
expect(result.executionTime).to.be.a('number');
|
||||
console.log(result.executionTime);
|
||||
});
|
||||
|
||||
it("should return timeout error when statement takes too long", async function() {
|
||||
const someFunction = async (done) => {
|
||||
while(true){
|
||||
// statement stuck in loop or too busy
|
||||
}
|
||||
}
|
||||
const result = await runScript(someFunction.toString(), true, {maxSyncStatementDuration: 300});
|
||||
|
||||
expect(result).to.be.ok;
|
||||
expect(result.success).to.be.false;
|
||||
expect(result.message).contains("Max. synchronous statement execution time exceeded");
|
||||
expect(result.status).eq("timeout");
|
||||
expect(result.executionTime).to.be.a('number');
|
||||
console.log(result.executionTime);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:15
|
||||
FROM node:16
|
||||
|
||||
#SET ENV Variables
|
||||
ENV PRODUCTION=true
|
||||
|
||||
30
accounts/Dockerfile.dev
Normal file
30
accounts/Dockerfile.dev
Normal file
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Accounts Dockerfile
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:16
|
||||
|
||||
#SET ENV Variables
|
||||
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
# Install nodemon
|
||||
RUN npm install nodemon -g
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package.json files
|
||||
COPY ./package.json /usr/src/app/package.json
|
||||
COPY ./package-lock.json /usr/src/app/package-lock.json
|
||||
|
||||
|
||||
# Install app dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Expose ports.
|
||||
# - 3003: accounts
|
||||
EXPOSE 3003
|
||||
|
||||
#Run the app
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
17963
accounts/package-lock.json
generated
17963
accounts/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,20 +4,19 @@
|
||||
"private": true,
|
||||
"homepage": "/",
|
||||
"dependencies": {
|
||||
"amplitude-js": "^5.8.0",
|
||||
"amplitude-js": "^8.3.1",
|
||||
"axios": "^0.21.1",
|
||||
"card-validator": "^4.3.0",
|
||||
"cli-table": "^0.3.1",
|
||||
"card-validator": "^8.1.1",
|
||||
"cli-table": "^0.3.6",
|
||||
"compression": "^1.7.4",
|
||||
"express": "^4.16.4",
|
||||
"faker": "^4.1.0",
|
||||
"file-saver": "^2.0.1",
|
||||
"faker": "^5.5.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"history": "^4.7.2",
|
||||
"jest": "^26.6.3",
|
||||
"loadable-components": "^2.2.3",
|
||||
"prop-types": "^15.6.1",
|
||||
"puppeteer": "^5.5.0",
|
||||
"puppeteer-cluster": "^0.22.0",
|
||||
"query-string": "^5.1.1",
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
@@ -33,11 +32,11 @@
|
||||
"redux-form": "^7.3.0",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"sane-email-validation": "^1.1.0",
|
||||
"sane-email-validation": "^3.0.1",
|
||||
"universal-cookie": "^4.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"valid-url": "^1.0.9",
|
||||
"workbox-build": "^4.3.1"
|
||||
"workbox-build": "^6.1.5"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "PORT=3003 react-scripts start",
|
||||
@@ -54,11 +53,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"chrome-launcher": "^0.13.2",
|
||||
"commander": "^4.0.1",
|
||||
"depcheck": "^0.9.2",
|
||||
"commander": "^7.2.0",
|
||||
"depcheck": "^1.4.1",
|
||||
"lighthouse": "^6.2.0",
|
||||
"npm-force-resolutions": "0.0.3",
|
||||
"ora": "^4.0.3",
|
||||
"npm-force-resolutions": "0.0.10",
|
||||
"ora": "^5.4.1",
|
||||
"should": "^13.2.3"
|
||||
},
|
||||
"resolutions": {},
|
||||
|
||||
@@ -4,7 +4,12 @@ import { history, isServer } from './store';
|
||||
import { connect } from 'react-redux';
|
||||
import { allRoutes } from './routes';
|
||||
import BackboneModals from './containers/BackboneModals';
|
||||
import { DASHBOARD_URL, ADMIN_DASHBOARD_URL, IS_SAAS_SERVICE } from './config';
|
||||
import {
|
||||
DASHBOARD_URL,
|
||||
ADMIN_DASHBOARD_URL,
|
||||
IS_SAAS_SERVICE,
|
||||
User,
|
||||
} from './config';
|
||||
import queryString from 'query-string';
|
||||
import ReactGA from 'react-ga';
|
||||
import Cookies from 'universal-cookie';
|
||||
@@ -25,6 +30,7 @@ const isStatusPageLogin =
|
||||
queryString.parse(window.location.search).statusPage === 'true';
|
||||
const statusPageURL = queryString.parse(window.location.search).statusPageURL;
|
||||
const userIsLoggedIn = cookies.get('data') || cookies.get('admin-data');
|
||||
const redirectTo = queryString.parse(window.location.search).redirectTo;
|
||||
|
||||
if (userIsLoggedIn) {
|
||||
const {
|
||||
@@ -35,6 +41,8 @@ if (userIsLoggedIn) {
|
||||
? ADMIN_DASHBOARD_URL
|
||||
: isStatusPageLogin
|
||||
? `${statusPageURL}?userId=${userId}&accessToken=${jwtAccessToken}`
|
||||
: redirectTo
|
||||
? redirectTo
|
||||
: DASHBOARD_URL;
|
||||
}
|
||||
|
||||
@@ -43,6 +51,13 @@ const App = ({
|
||||
checkIfMasterAdminExists,
|
||||
saveStatusPage,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
// store initialUrl in sessionStorage
|
||||
User.setInitialUrl(window.location.href);
|
||||
|
||||
// unset initialUrl when unmount
|
||||
return () => User.removeInitialUrl();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (!IS_SAAS_SERVICE && exists === null) {
|
||||
checkIfMasterAdminExists();
|
||||
|
||||
@@ -56,8 +56,10 @@ export function loginSuccess(user) {
|
||||
});
|
||||
}
|
||||
|
||||
if (user.redirect) {
|
||||
if (user.redirect && user?.tokens?.jwtAccessToken) {
|
||||
return (window.location = `${user.redirect}?accessToken=${user.tokens.jwtAccessToken}`);
|
||||
} else if (user.redirect) {
|
||||
return (window.location = user.redirect);
|
||||
} else if (user.role === 'master-admin') {
|
||||
window.location = ADMIN_DASHBOARD_URL;
|
||||
} else {
|
||||
|
||||
@@ -91,9 +91,15 @@ export const User = {
|
||||
setEmail(email) {
|
||||
localStorage.setItem('email', email);
|
||||
},
|
||||
|
||||
initialUrl() {
|
||||
return sessionStorage.getItem('initialUrl');
|
||||
},
|
||||
|
||||
setInitialUrl(url) {
|
||||
sessionStorage.setItem('initialUrl', url);
|
||||
},
|
||||
|
||||
setProject(project) {
|
||||
localStorage.setItem('project', project);
|
||||
},
|
||||
@@ -114,6 +120,10 @@ export const User = {
|
||||
localStorage.removeItem('token');
|
||||
},
|
||||
|
||||
removeInitialUrl() {
|
||||
return sessionStorage.removeItem('initialUrl');
|
||||
},
|
||||
|
||||
isLoggedIn() {
|
||||
return localStorage.getItem('access_token') ? true : false;
|
||||
},
|
||||
@@ -241,7 +251,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWIqpBpStiqQp',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Growth',
|
||||
@@ -255,21 +265,21 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWKiTdQ6NiQFw',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9Iox3l2YqLTDR',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IlBKhsFz4hV2',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
},
|
||||
];
|
||||
} else {
|
||||
@@ -286,7 +296,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoVgJu5PKMLRJU',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Growth',
|
||||
@@ -300,21 +310,21 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoViZshjqzZ0vv',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9Ii6Qj3HLdtty',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IjvX2Flsvlcg',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -19,10 +19,15 @@ export const history = isServer
|
||||
? createMemoryHistory({ initialEntries: [url] })
|
||||
: createBrowserHistory();
|
||||
|
||||
export const removeQuery = () => {
|
||||
export const removeQuery = removeField => {
|
||||
const location = Object.assign({}, history.location);
|
||||
const query = queryString.parse(location.search);
|
||||
if (!query.token) delete location.search;
|
||||
if (query[removeField]) delete query[removeField];
|
||||
|
||||
// remove "token" field - keeping this to prevent regression
|
||||
if (query['token']) delete query['token'];
|
||||
|
||||
location.search = queryString.stringify(query);
|
||||
history.push(location);
|
||||
};
|
||||
const initialState = {};
|
||||
|
||||
@@ -14,6 +14,7 @@ const buildSW = () => {
|
||||
.then(({ count, size }) => {
|
||||
// Optionally, log any warnings and details.
|
||||
return `${count} files will be precached, totaling ${size} bytes.`;
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
buildSW();
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
/* eslint-disable */
|
||||
if ('function' === typeof importScripts) {
|
||||
importScripts(
|
||||
'https://storage.googleapis.com/workbox-cdn/releases/3.5.0/workbox-sw.js'
|
||||
'https://storage.googleapis.com/workbox-cdn/releases/6.1.1/workbox-sw.js'
|
||||
);
|
||||
|
||||
/* global workbox */
|
||||
if (workbox) {
|
||||
const { skipWaiting, clientsClaim } = workbox.core;
|
||||
const { precacheAndRoute, cleanupOutdatedCaches } = workbox.precaching;
|
||||
|
||||
// skip waiting and switch to activating stage
|
||||
skipWaiting();
|
||||
// control webpage as soon as possible
|
||||
clientsClaim();
|
||||
// try to clean up old caches from previous versions
|
||||
cleanupOutdatedCaches();
|
||||
|
||||
/* injection point for manifest files. */
|
||||
workbox.precaching.precacheAndRoute([], {
|
||||
cleanURLs: false,
|
||||
});
|
||||
precacheAndRoute(self.__WB_MANIFEST, { cleanUrls: false });
|
||||
} else {
|
||||
console.log('Workbox could not be loaded. No Offline support');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:15
|
||||
FROM node:16
|
||||
|
||||
#SET ENV Variables
|
||||
ENV PRODUCTION=true
|
||||
|
||||
31
admin-dashboard/Dockerfile.dev
Normal file
31
admin-dashboard/Dockerfile.dev
Normal file
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# Admin Dashboard Dockerfile
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:16
|
||||
|
||||
#SET ENV Variables
|
||||
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
# Install nodemon
|
||||
RUN npm install nodemon -g
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package.json files
|
||||
COPY ./package.json /usr/src/app/package.json
|
||||
COPY ./package-lock.json /usr/src/app/package-lock.json
|
||||
|
||||
|
||||
# Install app dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Expose ports.
|
||||
# - 3100: Fyipe-admin-dashboard
|
||||
EXPOSE 3100
|
||||
|
||||
|
||||
#Run the app
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
13031
admin-dashboard/package-lock.json
generated
13031
admin-dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,20 +5,18 @@
|
||||
"dependencies": {
|
||||
"@trendmicro/react-dropdown": "^1.4.0",
|
||||
"axios": "^0.21.1",
|
||||
"card-validator": "^6.2.0",
|
||||
"clipboard": "^2.0.1",
|
||||
"card-validator": "^8.1.1",
|
||||
"clipboard": "^2.0.8",
|
||||
"express": "^4.16.4",
|
||||
"file-saver": "^2.0.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"font-awesome": "^4.7.0",
|
||||
"fuzzy-match-utils": "^1.3.0",
|
||||
"history": "^4.7.2",
|
||||
"history": "^5.0.0",
|
||||
"jest": "^25.2.4",
|
||||
"loadable-components": "^2.2.3",
|
||||
"mixpanel-browser": "^2.22.3",
|
||||
"moment": "^2.22.2",
|
||||
"prop-types": "^15.6.1",
|
||||
"puppeteer": "^5.5.0",
|
||||
"puppeteer-cluster": "^0.22.0",
|
||||
"react": "^16.14.0",
|
||||
"react-click-outside": "github:tj/react-click-outside",
|
||||
"react-dom": "^16.14.0",
|
||||
@@ -34,7 +32,7 @@
|
||||
"react-select-fyipe": "^2.1.8",
|
||||
"react-widgets": "^4.4.9",
|
||||
"redux": "^3.7.2",
|
||||
"redux-form": "^7.3.0",
|
||||
"redux-form": "^7.4.3",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"sane-email-validation": "^1.1.0",
|
||||
@@ -53,9 +51,9 @@
|
||||
"dep-check": "depcheck ./ --skip-missing=true --ignores='babel-*,loadable-components'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"depcheck": "^0.9.2",
|
||||
"jest-localstorage-mock": "^2.2.0",
|
||||
"npm-force-resolutions": "0.0.3",
|
||||
"depcheck": "^1.4.1",
|
||||
"jest-localstorage-mock": "^2.4.14",
|
||||
"npm-force-resolutions": "0.0.10",
|
||||
"should": "^13.2.3"
|
||||
},
|
||||
"resolutions": {},
|
||||
|
||||
@@ -381,6 +381,115 @@ export const blockUser = userId => async dispatch => {
|
||||
}
|
||||
};
|
||||
|
||||
//Enable Admin Mode
|
||||
export const enableAdminModeRequest = () => {
|
||||
return {
|
||||
type: types.ENABLE_ADMIN_MODE_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export const enableAdminModeSuccess = user => {
|
||||
return {
|
||||
type: types.ENABLE_ADMIN_MODE_SUCCESS,
|
||||
payload: user,
|
||||
};
|
||||
};
|
||||
|
||||
export const enableAdminModeError = error => {
|
||||
return {
|
||||
type: types.ENABLE_ADMIN_MODE_FAILED,
|
||||
payload: error,
|
||||
};
|
||||
};
|
||||
|
||||
export const enableAdminModeReset = () => {
|
||||
return {
|
||||
type: types.ENABLE_ADMIN_MODE_RESET,
|
||||
};
|
||||
};
|
||||
|
||||
// Enable admin mode
|
||||
export const enableAdminMode = (userId, values) => async dispatch => {
|
||||
dispatch(enableAdminModeRequest());
|
||||
|
||||
try {
|
||||
const response = await postApi(
|
||||
`user/${userId}/switchToAdminMode`,
|
||||
values
|
||||
);
|
||||
const data = response.data;
|
||||
|
||||
dispatch(enableAdminModeSuccess(data));
|
||||
return response;
|
||||
} catch (error) {
|
||||
let errorMsg;
|
||||
if (error && error.response && error.response.data)
|
||||
errorMsg = error.response.data;
|
||||
if (error && error.data) {
|
||||
errorMsg = error.data;
|
||||
}
|
||||
if (error && error.message) {
|
||||
errorMsg = error.message;
|
||||
} else {
|
||||
errorMsg = 'Network Error';
|
||||
}
|
||||
dispatch(enableAdminModeError(errors(errorMsg)));
|
||||
}
|
||||
};
|
||||
|
||||
//Disable Admin Mode
|
||||
export const disableAdminModeRequest = () => {
|
||||
return {
|
||||
type: types.DISABLE_ADMIN_MODE_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export const disableAdminModeSuccess = user => {
|
||||
return {
|
||||
type: types.DISABLE_ADMIN_MODE_SUCCESS,
|
||||
payload: user,
|
||||
};
|
||||
};
|
||||
|
||||
export const disableAdminModeError = error => {
|
||||
return {
|
||||
type: types.DISABLE_ADMIN_MODE_FAILED,
|
||||
payload: error,
|
||||
};
|
||||
};
|
||||
|
||||
export const disableAdminModeReset = () => {
|
||||
return {
|
||||
type: types.DISABLE_ADMIN_MODE_RESET,
|
||||
};
|
||||
};
|
||||
|
||||
// Disable admin mode
|
||||
export const disableAdminMode = userId => async dispatch => {
|
||||
dispatch(disableAdminModeRequest());
|
||||
|
||||
try {
|
||||
const response = await postApi(`user/${userId}/exitAdminMode`);
|
||||
const data = response.data;
|
||||
|
||||
dispatch(disableAdminModeSuccess(data));
|
||||
return response;
|
||||
} catch (error) {
|
||||
let errorMsg;
|
||||
if (error && error.response && error.response.data)
|
||||
errorMsg = error.response.data;
|
||||
if (error && error.data) {
|
||||
errorMsg = error.data;
|
||||
}
|
||||
if (error && error.message) {
|
||||
errorMsg = error.message;
|
||||
} else {
|
||||
errorMsg = 'Network Error';
|
||||
}
|
||||
dispatch(disableAdminModeError(errors(errorMsg)));
|
||||
}
|
||||
};
|
||||
|
||||
//Unblock user
|
||||
export const unblockUserRequest = () => {
|
||||
return {
|
||||
|
||||
@@ -478,7 +478,7 @@ export class CallLogsList extends Component {
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
{/* <div className="Box-root">
|
||||
<div className="Box-root">
|
||||
<button
|
||||
id="deleteLog"
|
||||
onClick={this.handleDelete}
|
||||
@@ -493,7 +493,7 @@ export class CallLogsList extends Component {
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import uuid from 'uuid';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
|
||||
import { ListLoader } from '../basic/Loader';
|
||||
import { openModal, closeModal } from '../../actions/modal';
|
||||
@@ -411,19 +412,25 @@ export class EmailLogsList extends Component {
|
||||
id="log-count"
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{this.props.emailLogs &&
|
||||
this.props.emailLogs.count
|
||||
? `Page ${
|
||||
this.props.page
|
||||
} of ${numberOfPages} (${this.props
|
||||
.emailLogs &&
|
||||
this.props.emailLogs.count} Log${
|
||||
this.props.emailLogs &&
|
||||
this.props.emailLogs.count === 1
|
||||
? ''
|
||||
: 's'
|
||||
})`
|
||||
: null}
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.emailLogs &&
|
||||
this.props.emailLogs.count
|
||||
}
|
||||
>
|
||||
Page {this.props.page} of{' '}
|
||||
{numberOfPages} (
|
||||
<span id="email-log-count">
|
||||
{this.props.emailLogs.count}
|
||||
</span>{' '}
|
||||
Log
|
||||
<ShouldRender
|
||||
if={this.props.emailLogs.count > 0}
|
||||
>
|
||||
s
|
||||
</ShouldRender>
|
||||
)
|
||||
</ShouldRender>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
@@ -478,7 +485,7 @@ export class EmailLogsList extends Component {
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
{/* <div className="Box-root">
|
||||
<div className="Box-root">
|
||||
<button
|
||||
id="deleteLog"
|
||||
onClick={this.handleDelete}
|
||||
@@ -493,7 +500,7 @@ export class EmailLogsList extends Component {
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,10 @@ class About extends Component {
|
||||
return (
|
||||
<tr key={i}>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
id="probe-version"
|
||||
>
|
||||
{probe.probeName} Version
|
||||
</span>
|
||||
</td>
|
||||
@@ -97,7 +100,10 @@ class About extends Component {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
id="server-version"
|
||||
>
|
||||
Server Version
|
||||
</span>
|
||||
</td>
|
||||
@@ -120,7 +126,10 @@ class About extends Component {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
id="admin-dashboard-version"
|
||||
>
|
||||
Admin Dashboard Version
|
||||
</span>
|
||||
</td>
|
||||
@@ -143,7 +152,10 @@ class About extends Component {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
id="dashboard-version"
|
||||
>
|
||||
Dashboard Version
|
||||
</span>
|
||||
</td>
|
||||
@@ -166,7 +178,10 @@ class About extends Component {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
id="docs-version"
|
||||
>
|
||||
Docs Version
|
||||
</span>
|
||||
</td>
|
||||
@@ -187,7 +202,10 @@ class About extends Component {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap"
|
||||
id="helm-version"
|
||||
>
|
||||
Helm Chart Version
|
||||
</span>
|
||||
</td>
|
||||
|
||||
@@ -169,7 +169,8 @@ export class ProjectList extends Component {
|
||||
(project, index) => {
|
||||
const projectOwner =
|
||||
project.users.find(
|
||||
user => user.role === 'Owner'
|
||||
user =>
|
||||
user.projectRole === 'Owner'
|
||||
) || {};
|
||||
let usersDetail;
|
||||
if (project.users.length > 0) {
|
||||
|
||||
@@ -93,7 +93,7 @@ const fields = [
|
||||
label: 'SMTP Secure',
|
||||
// eslint-disable-next-line react/display-name, react/prop-types
|
||||
component: ({ input: { value, onChange } }) => (
|
||||
<label className="Toggler-wrap">
|
||||
<label className="Toggler-wrap" id="label_smpt_secure">
|
||||
<input
|
||||
className="btn-toggler"
|
||||
checked={value}
|
||||
@@ -463,6 +463,7 @@ export class Component extends React.Component {
|
||||
</button>
|
||||
<button
|
||||
className="bs-Button bs-Button--blue"
|
||||
id="save-smpt-settings"
|
||||
disabled={settings && settings.requesting}
|
||||
type="submit"
|
||||
>
|
||||
@@ -515,7 +516,7 @@ function mapStateToProps(state) {
|
||||
settings: state.settings,
|
||||
initialValues: state.settings[settingsType],
|
||||
smtpForm: state.form['smtp-form'] || {},
|
||||
smtpTestForm: state.form['smtp-test-form'] || {}
|
||||
smtpTestForm: state.form['smtp-test-form'] || {},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { openModal, closeModal } from '../../actions/modal';
|
||||
import DeleteConfirmationModal from './DeleteConfirmationModal';
|
||||
import SmsLogsContentViewModal from './SmsLogsContentViewModal';
|
||||
import SmsLogsErrorViewModal from './SmsLogsErrorViewModal';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
|
||||
import { history } from '../../store';
|
||||
|
||||
@@ -432,19 +433,25 @@ export class SmsLogsList extends Component {
|
||||
id="log-count"
|
||||
className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--wrap"
|
||||
>
|
||||
{this.props.smsLogs &&
|
||||
this.props.smsLogs.count
|
||||
? `Page ${
|
||||
this.props.page
|
||||
} of ${numberOfPages} (${this.props
|
||||
.smsLogs &&
|
||||
this.props.smsLogs.count} Log${
|
||||
this.props.smsLogs &&
|
||||
this.props.smsLogs.count === 1
|
||||
? ''
|
||||
: 's'
|
||||
})`
|
||||
: null}
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.smsLogs &&
|
||||
this.props.smsLogs.count
|
||||
}
|
||||
>
|
||||
Page {this.props.page} of{' '}
|
||||
{numberOfPages} (
|
||||
<span id="sms-log-count">
|
||||
{this.props.smsLogs.count}
|
||||
</span>{' '}
|
||||
Log
|
||||
<ShouldRender
|
||||
if={this.props.smsLogs.count > 0}
|
||||
>
|
||||
s
|
||||
</ShouldRender>
|
||||
)
|
||||
</ShouldRender>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
@@ -499,7 +506,7 @@ export class SmsLogsList extends Component {
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
{/* <div className="Box-root">
|
||||
<div className="Box-root">
|
||||
<button
|
||||
id="deleteLog"
|
||||
onClick={this.handleDelete}
|
||||
@@ -514,7 +521,7 @@ export class SmsLogsList extends Component {
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { FormLoader } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { disableAdminMode } from '../../actions/user';
|
||||
|
||||
export class UserAdminModeDisableBox extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
const { disableAdminMode, userId } = this.props;
|
||||
disableAdminMode(userId);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isRequesting } = this.props;
|
||||
|
||||
return (
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<div className="bs-ContentSection Card-root Card-shadow--medium">
|
||||
<div className="Box-root">
|
||||
<div className="bs-ContentSection-content Box-root Box-divider--surface-bottom-1 Flex-flex Flex-alignItems--center Flex-justifyContent--spaceBetween Padding-horizontal--20 Padding-vertical--16">
|
||||
<div className="Box-root">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Disable Admin Mode</span>
|
||||
</span>
|
||||
<p>
|
||||
<span>
|
||||
Click the button to disable admin mode
|
||||
and revert to original user password
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="bs-ContentSection-footer bs-ContentSection-content Box-root Box-background--white Flex-flex Flex-alignItems--center Flex-justifyContent--spaceBetween Padding-horizontal--0 Padding-vertical--12">
|
||||
<span className="db-SettingsForm-footerMessage"></span>
|
||||
<div>
|
||||
<button
|
||||
id="block"
|
||||
className="bs-Button bs-Button--green Box-background--green"
|
||||
disabled={isRequesting}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
<ShouldRender if={!isRequesting}>
|
||||
<span>Dsiable Admin Mode</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<FormLoader />
|
||||
</ShouldRender>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UserAdminModeDisableBox.displayName = 'UserAdminModeDisableBox';
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ disableAdminMode }, dispatch);
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const userId = state.user.user.user ? state.user.user.user._id : null;
|
||||
|
||||
return {
|
||||
userId,
|
||||
isRequesting:
|
||||
state.user &&
|
||||
state.user.disableAdminMode &&
|
||||
state.user.disableAdminMode.requesting,
|
||||
};
|
||||
};
|
||||
|
||||
UserAdminModeDisableBox.propTypes = {
|
||||
isRequesting: PropTypes.oneOf([null, undefined, true, false]),
|
||||
disableAdminMode: PropTypes.func.isRequired,
|
||||
userId: PropTypes.string,
|
||||
};
|
||||
|
||||
UserAdminModeDisableBox.contextTypes = {
|
||||
mixpanel: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(UserAdminModeDisableBox);
|
||||
128
admin-dashboard/src/components/user/UserAdminModeEnableBox.js
Normal file
128
admin-dashboard/src/components/user/UserAdminModeEnableBox.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import uuid from 'uuid';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { FormLoader } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { enableAdminMode } from '../../actions/user';
|
||||
import UserAdminModeEnableModal from './UserAdminModeEnableModal';
|
||||
import { openModal, closeModal } from '../../actions/modal';
|
||||
|
||||
export class UserAdminModeEnableBox extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { AdminModeModalId: uuid.v4() };
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
const { enableAdminMode, userId } = this.props;
|
||||
const { AdminModeModalId } = this.state;
|
||||
|
||||
const thisObj = this;
|
||||
this.props.openModal({
|
||||
id: AdminModeModalId,
|
||||
onConfirm: values => {
|
||||
return enableAdminMode(userId, values).then(() => {
|
||||
if (window.location.href.indexOf('localhost') <= -1) {
|
||||
thisObj.context.mixpanel.track('Admin mode enabled');
|
||||
}
|
||||
});
|
||||
},
|
||||
content: UserAdminModeEnableModal,
|
||||
});
|
||||
};
|
||||
|
||||
handleKeyBoard = e => {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
return this.props.closeModal({
|
||||
id: this.state.AdminModeModalId,
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isRequesting } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
onKeyDown={this.handleKeyBoard}
|
||||
className="Box-root Margin-bottom--12"
|
||||
>
|
||||
<div className="bs-ContentSection Card-root Card-shadow--medium">
|
||||
<div className="Box-root">
|
||||
<div className="bs-ContentSection-content Box-root Box-divider--surface-bottom-1 Flex-flex Flex-alignItems--center Flex-justifyContent--spaceBetween Padding-horizontal--20 Padding-vertical--16">
|
||||
<div className="Box-root">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Enable Admin Mode</span>
|
||||
</span>
|
||||
<p>
|
||||
<span>
|
||||
Click the button to enable admin mode
|
||||
and create a temporary password for this
|
||||
user
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="bs-ContentSection-footer bs-ContentSection-content Box-root Box-background--white Flex-flex Flex-alignItems--center Flex-justifyContent--spaceBetween Padding-horizontal--0 Padding-vertical--12">
|
||||
<span className="db-SettingsForm-footerMessage"></span>
|
||||
<div>
|
||||
<button
|
||||
id="block"
|
||||
className="bs-Button bs-Button--red Box-background--red"
|
||||
disabled={isRequesting}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
<ShouldRender if={!isRequesting}>
|
||||
<span>Enable</span>
|
||||
</ShouldRender>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<FormLoader />
|
||||
</ShouldRender>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UserAdminModeEnableBox.displayName = 'UserAdminModeEnableBox';
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ enableAdminMode, openModal, closeModal }, dispatch);
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const userId = state.user.user.user ? state.user.user.user._id : null;
|
||||
|
||||
return {
|
||||
userId,
|
||||
isRequesting:
|
||||
state.user &&
|
||||
state.user.enableAdminMode &&
|
||||
state.user.enableAdminMode.requesting,
|
||||
};
|
||||
};
|
||||
|
||||
UserAdminModeEnableBox.propTypes = {
|
||||
isRequesting: PropTypes.oneOf([null, undefined, true, false]),
|
||||
enableAdminMode: PropTypes.func.isRequired,
|
||||
closeModal: PropTypes.func,
|
||||
openModal: PropTypes.func.isRequired,
|
||||
userId: PropTypes.string,
|
||||
};
|
||||
|
||||
UserAdminModeEnableBox.contextTypes = {
|
||||
mixpanel: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(UserAdminModeEnableBox);
|
||||
216
admin-dashboard/src/components/user/UserAdminModeEnableModal.js
Normal file
216
admin-dashboard/src/components/user/UserAdminModeEnableModal.js
Normal file
@@ -0,0 +1,216 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import ClickOutside from 'react-click-outside';
|
||||
import { Spinner } from '../basic/Loader';
|
||||
import ShouldRender from '../basic/ShouldRender';
|
||||
import { reduxForm, Field } from 'redux-form';
|
||||
import { RenderField } from '../basic/RenderField';
|
||||
import { ValidateField } from '../../config';
|
||||
|
||||
const formName = 'UserAdminModeEnableForm';
|
||||
|
||||
class UserAdminModeEnableModal extends Component {
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keydown', this.handleKeyboard);
|
||||
}
|
||||
|
||||
submitForm = values => {
|
||||
return this.props.confirmThisDialog(values);
|
||||
};
|
||||
|
||||
handleKeyboard = e => {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
return this.props.closeThisDialog();
|
||||
case 'Enter':
|
||||
return document.getElementById('enableAdminMode').click();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
isRequesting,
|
||||
enableAdminModeError,
|
||||
closeThisDialog,
|
||||
handleSubmit,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className="ModalLayer-wash Box-root Flex-flex Flex-alignItems--flexStart Flex-justifyContent--center">
|
||||
<div
|
||||
className="ModalLayer-contents"
|
||||
tabIndex={-1}
|
||||
style={{ marginTop: 40 }}
|
||||
>
|
||||
<div className="bs-BIM">
|
||||
<div className="bs-Modal bs-Modal--medium">
|
||||
<ClickOutside onClickOutside={closeThisDialog}>
|
||||
<div className="bs-Modal-header">
|
||||
<div className="bs-Modal-header-copy">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--20 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
<span>Enable Admin Mode</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
id={formName}
|
||||
onSubmit={handleSubmit(this.submitForm)}
|
||||
>
|
||||
<div className="bs-Modal-content">
|
||||
<span className="Text-color--inherit Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
|
||||
Enter a temporary password (min. 6
|
||||
characters) to use in Admin mode
|
||||
</span>
|
||||
<div className="bs-Fieldset-wrapper Box-root Margin-bottom--2">
|
||||
<fieldset className="Margin-bottom--16">
|
||||
<div className="bs-Fieldset-row">
|
||||
<label
|
||||
className="bs-Fieldset-label Text-align--left"
|
||||
htmlFor="temporaryPassword"
|
||||
>
|
||||
<span>
|
||||
Temporary Password
|
||||
</span>
|
||||
</label>
|
||||
<div className="bs-Fieldset-fields">
|
||||
<Field
|
||||
className="db-BusinessSettings-input TextInput bs-TextInput"
|
||||
component={
|
||||
RenderField
|
||||
}
|
||||
type="text"
|
||||
name={
|
||||
'temporaryPassword'
|
||||
}
|
||||
placeholder="Enter Password"
|
||||
disabled={
|
||||
isRequesting
|
||||
}
|
||||
validate={
|
||||
ValidateField.password6
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bs-Modal-footer">
|
||||
<div className="bs-Modal-footer-actions">
|
||||
<ShouldRender
|
||||
if={enableAdminModeError}
|
||||
>
|
||||
<div className="bs-Tail-copy">
|
||||
<div
|
||||
className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart"
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="Box-root Margin-right--8">
|
||||
<div
|
||||
className="Icon Icon--info Icon--color--red Icon--size--14 Box-root Flex-flex"
|
||||
style={{
|
||||
marginTop:
|
||||
'2px',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className="Box-root">
|
||||
<span
|
||||
style={{
|
||||
color:
|
||||
'red',
|
||||
}}
|
||||
>
|
||||
{
|
||||
enableAdminModeError
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<button
|
||||
className={`bs-Button btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="button"
|
||||
onClick={closeThisDialog}
|
||||
disabled={isRequesting}
|
||||
>
|
||||
<span>Cancel</span>
|
||||
<span className="cancel-btn__keycode">
|
||||
Esc
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
id="enableAdminMode"
|
||||
className={`bs-Button bs-Button--red Box-background--red btn__modal ${isRequesting &&
|
||||
'bs-is-disabled'}`}
|
||||
type="submit"
|
||||
disabled={isRequesting}
|
||||
autoFocus={true}
|
||||
>
|
||||
<ShouldRender if={isRequesting}>
|
||||
<Spinner />
|
||||
</ShouldRender>
|
||||
<span>Enable Admin Mode</span>
|
||||
<span className="delete-btn__keycode">
|
||||
<span className="keycode__icon keycode__icon--enter" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UserAdminModeEnableModal.displayName = 'UserAdminModeEnableModal';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
isRequesting:
|
||||
state.user &&
|
||||
state.user.enableAdminMode &&
|
||||
state.user.enableAdminMode.requesting,
|
||||
enableAdminModeError:
|
||||
state.user &&
|
||||
state.user.enableAdminMode &&
|
||||
state.user.enableAdminMode.error,
|
||||
};
|
||||
};
|
||||
|
||||
UserAdminModeEnableModal.propTypes = {
|
||||
isRequesting: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
confirmThisDialog: PropTypes.func.isRequired,
|
||||
closeThisDialog: PropTypes.func,
|
||||
enableAdminModeError: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.oneOf([null, undefined]),
|
||||
]),
|
||||
handleSubmit: PropTypes.func,
|
||||
};
|
||||
|
||||
const UserAdminModeEnableForm = reduxForm({
|
||||
form: formName,
|
||||
enableReinitialize: false,
|
||||
destroyOnUnmount: true,
|
||||
})(UserAdminModeEnableModal);
|
||||
|
||||
export default connect(mapStateToProps)(UserAdminModeEnableForm);
|
||||
@@ -54,6 +54,12 @@ const UserList = ({ users }) =>
|
||||
<span>Blocked</span>
|
||||
</span>
|
||||
</div>
|
||||
) : user.isAdminMode ? (
|
||||
<div className="Badge Badge--color--red Box-root Flex-inlineFlex Flex-alignItems--center Padding-horizontal--8 Padding-vertical--2">
|
||||
<span className="Badge-text Text-color--red Text-display--inline Text-fontSize--12 Text-fontWeight--bold Text-lineHeight--16 Text-typeface--upper Text-wrap--noWrap">
|
||||
<span>AdminMode</span>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="Badge Badge--color--green Box-root Flex-inlineFlex Flex-alignItems--center Padding-horizontal--8 Padding-vertical--2">
|
||||
<span className="Badge-text Text-color--green Text-display--inline Text-fontSize--12 Text-fontWeight--bold Text-lineHeight--16 Text-typeface--upper Text-wrap--noWrap">
|
||||
|
||||
@@ -78,6 +78,15 @@ export class UserSetting extends Component {
|
||||
<span>Blocked</span>
|
||||
</span>
|
||||
</div>
|
||||
) : this.props.user.isAdminMode ? (
|
||||
<div
|
||||
className="Badge Badge--color--red Box-root Flex-inlineFlex Flex-alignItems--center Padding-horizontal--8 Padding-vertical--2"
|
||||
style={{ float: 'right' }}
|
||||
>
|
||||
<span className="Badge-text Text-color--red Text-display--inline Text-fontSize--12 Text-fontWeight--bold Text-lineHeight--16 Text-typeface--upper Text-wrap--noWrap">
|
||||
<span>AdminMode</span>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="Badge Badge--color--green Box-root Flex-inlineFlex Flex-alignItems--center Padding-horizontal--8 Padding-vertical--2"
|
||||
|
||||
@@ -32,8 +32,7 @@ export class UserUnblockBox extends Component {
|
||||
</span>
|
||||
<p>
|
||||
<span>
|
||||
Click the button to unblock this
|
||||
project.
|
||||
Click the button to unblock this user.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -290,6 +290,13 @@ export const ValidateField = {
|
||||
|
||||
compare: (text1, text2) =>
|
||||
text1 === text2 ? undefined : 'These texts donot match',
|
||||
|
||||
password6: password =>
|
||||
!password || !password.length
|
||||
? 'Password cannot be blank'
|
||||
: password.length < 6
|
||||
? 'Password must be a minimum of 6 characters'
|
||||
: undefined,
|
||||
};
|
||||
|
||||
export const PricingPlan = {
|
||||
@@ -311,7 +318,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWIqpBpStiqQp',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Growth',
|
||||
@@ -325,21 +332,21 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoWKiTdQ6NiQFw',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9Iox3l2YqLTDR',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IlBKhsFz4hV2',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
},
|
||||
];
|
||||
} else {
|
||||
@@ -356,7 +363,7 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoVgJu5PKMLRJU',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Growth',
|
||||
@@ -370,21 +377,21 @@ export const PricingPlan = {
|
||||
planId: 'plan_GoViZshjqzZ0vv',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9Ii6Qj3HLdtty',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
},
|
||||
{
|
||||
category: 'Scale',
|
||||
planId: 'plan_H9IjvX2Flsvlcg',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -50,6 +50,18 @@ export const UNBLOCK_USER_RESET = 'UNBLOCK_USER_RESET';
|
||||
export const UNBLOCK_USER_SUCCESS = 'UNBLOCK_USER_SUCCESS';
|
||||
export const UNBLOCK_USER_FAILED = 'UNBLOCK_USER_FAILED';
|
||||
|
||||
// Enable Admin Mode
|
||||
export const ENABLE_ADMIN_MODE_REQUEST = 'ENABLE_ADMIN_MODE_REQUEST';
|
||||
export const ENABLE_ADMIN_MODE_RESET = 'ENABLE_ADMIN_MODE_RESET';
|
||||
export const ENABLE_ADMIN_MODE_SUCCESS = 'ENABLE_ADMIN_MODE_SUCCESS';
|
||||
export const ENABLE_ADMIN_MODE_FAILED = 'ENABLE_ADMIN_MODE_FAILED';
|
||||
|
||||
// Disable Admin Mode
|
||||
export const DISABLE_ADMIN_MODE_REQUEST = 'DISABLE_ADMIN_MODE_REQUEST';
|
||||
export const DISABLE_ADMIN_MODE_RESET = 'DISABLE_ADMIN_MODE_RESET';
|
||||
export const DISABLE_ADMIN_MODE_SUCCESS = 'DISABLE_ADMIN_MODE_SUCCESS';
|
||||
export const DISABLE_ADMIN_MODE_FAILED = 'DISABLE_ADMIN_MODE_FAILED';
|
||||
|
||||
// Admin User Note
|
||||
export const ADD_USER_NOTE_REQUEST = 'ADD_USER_NOTE_REQUEST';
|
||||
export const ADD_USER_NOTE_RESET = 'ADD_USER_NOTE_RESET';
|
||||
|
||||
@@ -14,6 +14,9 @@ import UserUnblockBox from '../components/user/UserUnblockBox';
|
||||
import AdminNotes from '../components/adminNote/AdminNotes';
|
||||
import { fetchUserProjects } from '../actions/project';
|
||||
import { addUserNote, fetchUser, fetchUserloginHistory } from '../actions/user';
|
||||
import UserAdminModeEnableBox from '../components/user/UserAdminModeEnableBox';
|
||||
import UserAdminModeDisableBox from '../components/user/UserAdminModeDisableBox';
|
||||
import { User as LsUser } from '../config';
|
||||
|
||||
class User extends Component {
|
||||
componentDidMount() {
|
||||
@@ -61,6 +64,39 @@ class User extends Component {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<ShouldRender
|
||||
if={
|
||||
LsUser.getUserId() !==
|
||||
this.props.match.params
|
||||
.userId
|
||||
}
|
||||
>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<ShouldRender
|
||||
if={
|
||||
!this.props
|
||||
?.user
|
||||
?.isAdminMode &&
|
||||
!this.props
|
||||
?.user
|
||||
.deleted
|
||||
}
|
||||
>
|
||||
<UserAdminModeEnableBox />
|
||||
</ShouldRender>
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props?.user
|
||||
?.isAdminMode &&
|
||||
!this.props
|
||||
?.user
|
||||
.deleted
|
||||
}
|
||||
>
|
||||
<UserAdminModeDisableBox />
|
||||
</ShouldRender>
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<UserHistory
|
||||
history={
|
||||
@@ -74,49 +110,59 @@ class User extends Component {
|
||||
</div>
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.user &&
|
||||
!this.props.user
|
||||
.deleted &&
|
||||
!this.props.user
|
||||
.isBlocked
|
||||
LsUser.getUserId() !==
|
||||
this.props.match.params
|
||||
.userId
|
||||
}
|
||||
>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<UserBlockBox />
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.user &&
|
||||
!this.props.user
|
||||
.deleted &&
|
||||
this.props.user
|
||||
.isBlocked
|
||||
}
|
||||
>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<UserUnblockBox />
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.user &&
|
||||
!this.props.user.deleted
|
||||
}
|
||||
>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<UserDeleteBox />
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.user &&
|
||||
this.props.user.deleted
|
||||
}
|
||||
>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<UserRestoreBox />
|
||||
</div>
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.user &&
|
||||
!this.props.user
|
||||
.deleted &&
|
||||
!this.props.user
|
||||
.isBlocked
|
||||
}
|
||||
>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<UserBlockBox />
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.user &&
|
||||
!this.props.user
|
||||
.deleted &&
|
||||
this.props.user
|
||||
.isBlocked
|
||||
}
|
||||
>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<UserUnblockBox />
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.user &&
|
||||
!this.props.user
|
||||
.deleted
|
||||
}
|
||||
>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<UserDeleteBox />
|
||||
</div>
|
||||
</ShouldRender>
|
||||
<ShouldRender
|
||||
if={
|
||||
this.props.user &&
|
||||
this.props.user
|
||||
.deleted
|
||||
}
|
||||
>
|
||||
<div className="Box-root Margin-bottom--12">
|
||||
<UserRestoreBox />
|
||||
</div>
|
||||
</ShouldRender>
|
||||
</ShouldRender>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -46,6 +46,14 @@ import {
|
||||
FETCH_USER_LOGIN_HISTORY_FAILURE,
|
||||
FETCH_USER_LOGIN_HISTORY_SUCCESS,
|
||||
FETCH_USER_LOGIN_HISTORY_REQUEST,
|
||||
ENABLE_ADMIN_MODE_SUCCESS,
|
||||
ENABLE_ADMIN_MODE_REQUEST,
|
||||
ENABLE_ADMIN_MODE_FAILED,
|
||||
ENABLE_ADMIN_MODE_RESET,
|
||||
DISABLE_ADMIN_MODE_SUCCESS,
|
||||
DISABLE_ADMIN_MODE_REQUEST,
|
||||
DISABLE_ADMIN_MODE_FAILED,
|
||||
DISABLE_ADMIN_MODE_RESET,
|
||||
} from '../constants/user';
|
||||
|
||||
const INITIAL_STATE = {
|
||||
@@ -90,6 +98,11 @@ const INITIAL_STATE = {
|
||||
requesting: false,
|
||||
success: false,
|
||||
},
|
||||
enableAdminMode: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
success: false,
|
||||
},
|
||||
unblockUser: {
|
||||
error: null,
|
||||
requesting: false,
|
||||
@@ -414,6 +427,90 @@ export default function user(state = INITIAL_STATE, action) {
|
||||
},
|
||||
});
|
||||
|
||||
case ENABLE_ADMIN_MODE_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
enableAdminMode: {
|
||||
requesting: false,
|
||||
success: true,
|
||||
error: null,
|
||||
},
|
||||
user: {
|
||||
requesting: false,
|
||||
error: null,
|
||||
success: true,
|
||||
user: action.payload,
|
||||
},
|
||||
});
|
||||
|
||||
case ENABLE_ADMIN_MODE_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
enableAdminMode: {
|
||||
requesting: true,
|
||||
success: false,
|
||||
error: null,
|
||||
},
|
||||
});
|
||||
|
||||
case ENABLE_ADMIN_MODE_FAILED:
|
||||
return Object.assign({}, state, {
|
||||
enableAdminMode: {
|
||||
requesting: false,
|
||||
success: false,
|
||||
error: action.payload,
|
||||
},
|
||||
});
|
||||
|
||||
case ENABLE_ADMIN_MODE_RESET:
|
||||
return Object.assign({}, state, {
|
||||
enableAdminMode: {
|
||||
requesting: false,
|
||||
success: false,
|
||||
error: null,
|
||||
},
|
||||
});
|
||||
|
||||
case DISABLE_ADMIN_MODE_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
disableAdminMode: {
|
||||
requesting: false,
|
||||
success: true,
|
||||
error: null,
|
||||
},
|
||||
user: {
|
||||
requesting: false,
|
||||
error: null,
|
||||
success: true,
|
||||
user: action.payload,
|
||||
},
|
||||
});
|
||||
|
||||
case DISABLE_ADMIN_MODE_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
disableAdminMode: {
|
||||
requesting: true,
|
||||
success: false,
|
||||
error: null,
|
||||
},
|
||||
});
|
||||
|
||||
case DISABLE_ADMIN_MODE_FAILED:
|
||||
return Object.assign({}, state, {
|
||||
disableAdminMode: {
|
||||
requesting: false,
|
||||
success: false,
|
||||
error: action.payload,
|
||||
},
|
||||
});
|
||||
|
||||
case DISABLE_ADMIN_MODE_RESET:
|
||||
return Object.assign({}, state, {
|
||||
disableAdminMode: {
|
||||
requesting: false,
|
||||
success: false,
|
||||
error: null,
|
||||
},
|
||||
});
|
||||
|
||||
case UNBLOCK_USER_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
unblockUser: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Pull base image nodejs image.
|
||||
FROM node:15
|
||||
FROM node:16
|
||||
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
@@ -15,7 +15,7 @@ RUN npm ci --only=production
|
||||
COPY . /usr/src/app
|
||||
|
||||
# Expose ports.
|
||||
# - 1445: Fyipe Dcos
|
||||
# - 1445: Fyipe Docs
|
||||
EXPOSE 1445
|
||||
|
||||
#Run the app
|
||||
|
||||
30
api-docs/Dockerfile.dev
Normal file
30
api-docs/Dockerfile.dev
Normal file
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Fyipe Docs Dockerfile
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:16
|
||||
|
||||
#SET ENV Variables
|
||||
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
|
||||
# Install nodemon
|
||||
RUN npm install nodemon -g
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package.json files
|
||||
COPY ./package.json /usr/src/app/package.json
|
||||
COPY ./package-lock.json /usr/src/app/package-lock.json
|
||||
|
||||
|
||||
# Install app dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Expose ports.
|
||||
# - 3000: Fyipe
|
||||
EXPOSE 3000
|
||||
|
||||
#Run the app
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
128
api-docs/package-lock.json
generated
128
api-docs/package-lock.json
generated
@@ -29,7 +29,6 @@
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
@@ -42,11 +41,6 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mocha/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/url-parse-lax": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
|
||||
@@ -598,12 +592,6 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/undefsafe/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
@@ -1872,11 +1860,6 @@
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
@@ -2001,11 +1984,6 @@
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/mocha/node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
},
|
||||
"node_modules/log-symbols/node_modules/chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
@@ -2136,18 +2114,6 @@
|
||||
"semver-compare": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"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/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
@@ -2367,12 +2333,6 @@
|
||||
"node": ">=0.4.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/normalize-url": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
|
||||
@@ -2581,14 +2541,6 @@
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||
},
|
||||
"node_modules/wide-align/node_modules/is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/unique-string": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
||||
@@ -2965,17 +2917,6 @@
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/wide-align/node_modules/strip-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
@@ -3155,8 +3096,7 @@
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
@@ -3576,14 +3516,6 @@
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"node_modules/wide-align/node_modules/ansi-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mocha/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
@@ -3903,7 +3835,6 @@
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
@@ -4588,11 +4519,6 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
@@ -5282,11 +5208,6 @@
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
},
|
||||
"cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
@@ -5305,11 +5226,6 @@
|
||||
"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=="
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
@@ -5931,8 +5847,7 @@
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
@@ -6091,12 +6006,6 @@
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6256,16 +6165,6 @@
|
||||
"string-width": "^1.0.2 || 2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
@@ -6274,14 +6173,6 @@
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6318,21 +6209,6 @@
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -256,8 +256,8 @@
|
||||
"{{userId}} : Unique identifier for user account.",
|
||||
"{{projectId}} : Unique identifier for the current project."
|
||||
],
|
||||
"emailType": "Subscriber Incident Acknowldeged",
|
||||
"subject": "{{projectName}}/{{monitorName}}: Incident Acknowldeged",
|
||||
"emailType": "Subscriber Incident Acknowledged",
|
||||
"subject": "{{projectName}}/{{monitorName}}: Incident Acknowledged",
|
||||
"body": "..."
|
||||
},
|
||||
{
|
||||
@@ -661,8 +661,8 @@
|
||||
"{{userId}} : Unique identifier for user account.",
|
||||
"{{projectId}} : Unique identifier for the current project."
|
||||
],
|
||||
"emailType": "Subscriber Incident Acknowldeged",
|
||||
"subject": "{{projectName}}/{{monitorName}}: Incident Acknowldeged",
|
||||
"emailType": "Subscriber Incident Acknowledged",
|
||||
"subject": "{{projectName}}/{{monitorName}}: Incident Acknowledged",
|
||||
"body": "..."
|
||||
},
|
||||
{
|
||||
@@ -795,8 +795,8 @@
|
||||
"{{userId}} : Unique identifier for user account.",
|
||||
"{{projectId}} : Unique identifier for the current project."
|
||||
],
|
||||
"emailType": "Subscriber Incident Acknowldeged",
|
||||
"subject": "{{projectName}}/{{monitorName}}: Incident Acknowldeged",
|
||||
"emailType": "Subscriber Incident Acknowledged",
|
||||
"subject": "{{projectName}}/{{monitorName}}: Incident Acknowledged",
|
||||
"body": "..."
|
||||
},
|
||||
{
|
||||
@@ -857,8 +857,8 @@
|
||||
"{{userId}} : Unique identifier for user account.",
|
||||
"{{projectId}} : Unique identifier for the current project."
|
||||
],
|
||||
"emailType": "Subscriber Incident Acknowldeged",
|
||||
"subject": "{{projectName}}/{{monitorName}}: Incident Acknowldeged",
|
||||
"emailType": "Subscriber Incident Acknowledged",
|
||||
"subject": "{{projectName}}/{{monitorName}}: Incident Acknowledged",
|
||||
"body": "..."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
"{{projectName}} : Name of the project on which the incident has occured.",
|
||||
"{{incidentType}} : Type of incident."
|
||||
],
|
||||
"smsType": "Subscriber Incident Acknowldeged",
|
||||
"smsType": "Subscriber Incident Acknowledged",
|
||||
"body": "{{projectName}}/{{monitorName}} is {{incidentType}} at {{incidentTime}}. You are receiving this message because you subscribed to this monitor."
|
||||
},
|
||||
{
|
||||
@@ -615,7 +615,7 @@
|
||||
"{{projectName}} : Name of the project on which the incident has occured.",
|
||||
"{{incidentType}} : Type of incident."
|
||||
],
|
||||
"smsType": "Subscriber Incident Acknowldeged",
|
||||
"smsType": "Subscriber Incident Acknowledged",
|
||||
"body": "{{projectName}}/{{monitorName}} is {{incidentType}} at {{incidentTime}}. You are receiving this message because you subscribed to this monitor."
|
||||
},
|
||||
{
|
||||
@@ -737,7 +737,7 @@
|
||||
"{{projectName}} : Name of the project on which the incident has occured.",
|
||||
"{{incidentType}} : Type of incident."
|
||||
],
|
||||
"smsType": "Subscriber Incident Acknowldeged",
|
||||
"smsType": "Subscriber Incident Acknowledged",
|
||||
"body": "{{projectName}}/{{monitorName}} is {{incidentType}} at {{incidentTime}}. You are receiving this message because you subscribed to this monitor."
|
||||
},
|
||||
{
|
||||
@@ -782,7 +782,7 @@
|
||||
"{{projectName}} : Name of the project on which the incident has occured.",
|
||||
"{{incidentType}} : Type of incident."
|
||||
],
|
||||
"smsType": "Subscriber Incident Acknowldeged",
|
||||
"smsType": "Subscriber Incident Acknowledged",
|
||||
"body": "{{projectName}}/{{monitorName}} is {{incidentType}} at {{incidentTime}}. You are receiving this message because you subscribed to this monitor."
|
||||
},
|
||||
{
|
||||
|
||||
12
application-scanner/.dockerignore
Executable file
12
application-scanner/.dockerignore
Executable file
@@ -0,0 +1,12 @@
|
||||
node_modules/
|
||||
kubernetes/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
yarn.lock
|
||||
4
application-scanner/.env
Normal file
4
application-scanner/.env
Normal file
@@ -0,0 +1,4 @@
|
||||
CLUSTER_KEY=f414c23b4cdf4e84a6a66ecfd528eff2
|
||||
APPLICATION_SCANNER_NAME=US
|
||||
APPLICATION_SCANNER_KEY=33b674ca-9fdd-11e9-a2a3-2a2ae2dbccez
|
||||
SERVER_URL=http://localhost:3002
|
||||
1
application-scanner/.gitattributes
vendored
Normal file
1
application-scanner/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.js text eol=lf
|
||||
3
application-scanner/.gitignore
vendored
Executable file
3
application-scanner/.gitignore
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
kubernetes
|
||||
debug.log
|
||||
27
application-scanner/Dockerfile
Executable file
27
application-scanner/Dockerfile
Executable file
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# Fyipe-backend Dockerfile
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:16
|
||||
|
||||
#SET ENV Variables
|
||||
ENV PRODUCTION=true
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY package*.json /usr/src/app/
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Bundle app source
|
||||
COPY . /usr/src/app
|
||||
|
||||
# Expose ports.
|
||||
# - 3005: Application Scanner
|
||||
EXPOSE 3005
|
||||
|
||||
#Run the app
|
||||
CMD [ "npm", "start"]
|
||||
26
application-scanner/Dockerfile.dev
Normal file
26
application-scanner/Dockerfile.dev
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Fyipe-backend Dockerfile
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:16
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
RUN cd /usr/src/app
|
||||
|
||||
# Copy package.json files
|
||||
COPY ./package.json /usr/src/app/package.json
|
||||
COPY ./package-lock.json /usr/src/app/package-lock.json
|
||||
|
||||
|
||||
RUN npm ci
|
||||
|
||||
# Expose ports.
|
||||
# - 3005: Application Scanner
|
||||
EXPOSE 3005
|
||||
|
||||
#Run the app
|
||||
CMD [ "npm", "run", "dev"]
|
||||
|
||||
75
application-scanner/index.js
Normal file
75
application-scanner/index.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const { NODE_ENV } = process.env;
|
||||
|
||||
if(!NODE_ENV || NODE_ENV === 'development'){
|
||||
// Load env vars from /backend/.env
|
||||
require('custom-env').env();
|
||||
}
|
||||
|
||||
process.on('exit', () => {
|
||||
/* eslint-disable no-console */
|
||||
console.log('Application Scanner Shutting Shutdown');
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', err => {
|
||||
/* eslint-disable no-console */
|
||||
console.error('Unhandled rejection in application scanner process occurred');
|
||||
/* eslint-disable no-console */
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', err => {
|
||||
/* eslint-disable no-console */
|
||||
console.error('Uncaught exception in application scanner process occurred');
|
||||
/* eslint-disable no-console */
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const http = require('http').createServer(app);
|
||||
const cors = require('cors');
|
||||
const Main = require('./worker/main');
|
||||
const cron = require('node-cron');
|
||||
const config = require('./utils/config');
|
||||
|
||||
const cronApplicationSecurityStartTime = Math.floor(Math.random() * 50);
|
||||
|
||||
app.use(cors());
|
||||
app.set('port', process.env.PORT || 3005);
|
||||
|
||||
http.listen(app.get('port'), function() {
|
||||
// eslint-disable-next-line
|
||||
console.log(
|
||||
`Application Scanner ${config.applicationScannerName} and Application Key ${
|
||||
config.applicationScannerKey
|
||||
} Started on port ${app.get('port')}. Fyipe API URL: ${
|
||||
config.serverUrl
|
||||
}`
|
||||
);
|
||||
});
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.send(
|
||||
JSON.stringify({
|
||||
status: 200,
|
||||
message: 'Service Status - OK',
|
||||
serviceType: 'fyipe-applicationScanner',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
//App Version
|
||||
app.get(['/application/version', '/version'], function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.send({ applicationScannerVersion: process.env.npm_package_version });
|
||||
});
|
||||
|
||||
// Run this cron at 3 AM once a day.
|
||||
cron.schedule('0 3 * * *', () => {
|
||||
setTimeout(() => {
|
||||
Main.runApplicationScan();
|
||||
}, cronApplicationSecurityStartTime * 1000);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
4745
application-scanner/package-lock.json
generated
Normal file
4745
application-scanner/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
application-scanner/package.json
Normal file
28
application-scanner/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "applicationscanner",
|
||||
"version": "3.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node --max-http-header-size=80000 index.js",
|
||||
"dev": "nodemon --inspect=0.0.0.0 --max-http-header-size=80000 index.js"
|
||||
},
|
||||
"author": "David Adewole",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"cors": "^2.8.5",
|
||||
"custom-env": "^2.0.1",
|
||||
"express": "^4.17.1",
|
||||
"node-cron": "^3.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-ssh": "^11.1.1",
|
||||
"ping": "^0.4.1",
|
||||
"winston": "^3.3.3",
|
||||
"winston-slack-transport": "^2.0.0"
|
||||
}
|
||||
}
|
||||
109
application-scanner/utils/api.js
Executable file
109
application-scanner/utils/api.js
Executable file
@@ -0,0 +1,109 @@
|
||||
const axios = require('axios');
|
||||
const config = require('./config');
|
||||
|
||||
const _this = {
|
||||
getHeaders: () => {
|
||||
return {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
applicationScannerName: config.applicationScannerName,
|
||||
applicationScannerKey: config.applicationScannerKey,
|
||||
clusterKey: config.clusterKey,
|
||||
applicationScannerVersion: config.applicationScannerVersion,
|
||||
};
|
||||
},
|
||||
postApi: (url, data) => {
|
||||
const headers = _this.getHeaders();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: `${config.serverUrl}/${url}`,
|
||||
headers,
|
||||
data,
|
||||
})
|
||||
.then(function(response) {
|
||||
resolve(response.data);
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (error && error.response && error.response.data)
|
||||
error = error.response.data;
|
||||
if (error && error.data) {
|
||||
error = error.data;
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getApi: url => {
|
||||
const headers = _this.getHeaders();
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: 'GET',
|
||||
url: `${config.serverUrl}/${url}`,
|
||||
headers,
|
||||
})
|
||||
.then(function(response) {
|
||||
resolve(response.data);
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (error && error.response && error.response.data)
|
||||
error = error.response.data;
|
||||
if (error && error.data) {
|
||||
error = error.data;
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
putApi: (url, data) => {
|
||||
const headers = _this.getHeaders();
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: 'PUT',
|
||||
url: `${config.serverUrl}/${url}`,
|
||||
headers,
|
||||
data,
|
||||
})
|
||||
.then(function(response) {
|
||||
resolve(response.data);
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (error && error.response && error.response.data)
|
||||
error = error.response.data;
|
||||
if (error && error.data) {
|
||||
error = error.data;
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
deleteApi: (url, data) => {
|
||||
const headers = _this.getHeaders();
|
||||
return new Promise((resolve, reject) => {
|
||||
axios({
|
||||
method: 'DELETE',
|
||||
url: `${config.serverUrl}/${url}`,
|
||||
headers,
|
||||
data,
|
||||
})
|
||||
.then(function(response) {
|
||||
resolve(response.data);
|
||||
})
|
||||
.catch(function(error) {
|
||||
if (error && error.response && error.response.data)
|
||||
error = error.response.data;
|
||||
if (error && error.data) {
|
||||
error = error.data;
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = _this;
|
||||
@@ -2,6 +2,6 @@ const { postApi } = require('./api');
|
||||
|
||||
module.exports = {
|
||||
scan: async function(security) {
|
||||
return await postApi(`probe/scan/git`, { security });
|
||||
return await postApi(`application/scan/git`, { security });
|
||||
},
|
||||
};
|
||||
46
application-scanner/utils/config.js
Normal file
46
application-scanner/utils/config.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const packageJson = require('../package.json');
|
||||
|
||||
const COMMAND = {
|
||||
linux: {
|
||||
load: "top -b -n 2 | egrep --color 'load average|%Cpu'",
|
||||
cpu: "egrep --color 'processor|cores' /proc/cpuinfo",
|
||||
mem: "egrep --color 'Mem|Swap' /proc/meminfo",
|
||||
disk: "df -h | egrep --color '/dev/xvda1|/dev/sda7|/dev/nvme0n1p1'",
|
||||
temp: "sensors | egrep --color 'CPU'",
|
||||
},
|
||||
darwin: {
|
||||
load: "top -l 1 | egrep --color 'Load Avg|CPU usage'",
|
||||
cpu: 'sysctl -n machdep.cpu.core_count',
|
||||
mem: {
|
||||
used: "top -l 1 | egrep --color 'PhysMem'",
|
||||
total: 'sysctl -n hw.memsize',
|
||||
swap: 'sysctl -n vm.swapusage',
|
||||
},
|
||||
disk: "df -h | egrep --color '/dev/disk1s2'",
|
||||
temp: 'sysctl -n machdep.xcpm.cpu_thermal_level',
|
||||
},
|
||||
win: {
|
||||
load: 'wmic cpu get loadpercentage',
|
||||
cpu: 'wmic cpu get numberofcores',
|
||||
mem: {
|
||||
free: 'wmic os get freephysicalmemory',
|
||||
total: 'wmic computersystem get totalphysicalmemory',
|
||||
totalSwap: 'wmic os get totalvirtualmemorySize',
|
||||
freeSwap: 'wmic os get freevirtualmemory',
|
||||
},
|
||||
disk: {
|
||||
total: 'wmic logicaldisk get size',
|
||||
free: 'wmic logicaldisk get freespace',
|
||||
},
|
||||
temp: 'wmic computersystem get thermalstate',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
COMMAND,
|
||||
serverUrl: process.env['SERVER_URL'],
|
||||
applicationScannerName: process.env['APPLICATION_SCANNER_NAME'],
|
||||
applicationScannerKey: process.env['APPLICATION_SCANNER_KEY'],
|
||||
clusterKey: process.env['CLUSTER_KEY'],
|
||||
applicationScannerVersion: packageJson.version,
|
||||
};
|
||||
32
application-scanner/utils/errorService.js
Executable file
32
application-scanner/utils/errorService.js
Executable file
@@ -0,0 +1,32 @@
|
||||
const winston = require('winston');
|
||||
const Slack = require('winston-slack-transport');
|
||||
|
||||
if (
|
||||
process.env.PORT &&
|
||||
process.env.SLACK_ERROR_LOG_WEBHOOK &&
|
||||
process.env.SLACK_ERROR_LOG_CHANNEL
|
||||
) {
|
||||
winston.add(Slack, {
|
||||
webhook_url: process.env.SLACK_ERROR_LOG_WEBHOOK,
|
||||
channel: '#' + process.env.SLACK_ERROR_LOG_CHANNEL,
|
||||
username: 'Error Bot',
|
||||
handleExceptions: true,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
log: (functionName, error) => {
|
||||
error = error && error.message ? error.message : error;
|
||||
winston.error(
|
||||
JSON.stringify(
|
||||
{
|
||||
error: String(error),
|
||||
functionName: String(functionName),
|
||||
stack: new Error().stack,
|
||||
},
|
||||
0,
|
||||
2
|
||||
)
|
||||
);
|
||||
},
|
||||
};
|
||||
58
application-scanner/utils/fsHandlers.js
Normal file
58
application-scanner/utils/fsHandlers.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const fs = require('fs');
|
||||
const Path = require('path');
|
||||
const { promisify } = require('util');
|
||||
const readdir = promisify(fs.readdir);
|
||||
const rmdir = promisify(fs.rmdir);
|
||||
const unlink = promisify(fs.unlink);
|
||||
|
||||
/**
|
||||
* @description a promise based utility to read content of a file
|
||||
* @param {string} filePath path to file
|
||||
*/
|
||||
function readFileContent(filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.readFile(filePath, { encoding: 'utf8' }, function(error, data) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description an asynchronous function to handle deleting a file
|
||||
* @param {string} file path to file
|
||||
*/
|
||||
async function deleteFile(file) {
|
||||
if (fs.existsSync(file)) {
|
||||
await unlink(file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description a promise based utility to handle deleting a folder and it's content
|
||||
* @param {string} dir directory with or without file
|
||||
*/
|
||||
async function deleteFolderRecursive(dir) {
|
||||
if (fs.existsSync(dir)) {
|
||||
const entries = await readdir(dir, { withFileTypes: true });
|
||||
await Promise.all(
|
||||
entries.map(entry => {
|
||||
const fullPath = Path.join(dir, entry.name);
|
||||
return entry.isDirectory()
|
||||
? deleteFolderRecursive(fullPath)
|
||||
: unlink(fullPath);
|
||||
})
|
||||
);
|
||||
await rmdir(dir); // finally remove now empty directory
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readFileContent,
|
||||
deleteFile,
|
||||
deleteFolderRecursive,
|
||||
};
|
||||
27
application-scanner/worker/main.js
Executable file
27
application-scanner/worker/main.js
Executable file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
*
|
||||
* Copyright HackerBay, Inc.
|
||||
*
|
||||
*/
|
||||
const getApi = require('../utils/api').getApi;
|
||||
const ErrorService = require('../utils/errorService');
|
||||
const ApplicationSecurity = require('./applicationSecurity');
|
||||
|
||||
module.exports = {
|
||||
runApplicationScan: async function() {
|
||||
try {
|
||||
const securities = await getApi('application/applicationSecurities');
|
||||
if (securities && securities.length > 0) {
|
||||
await Promise.all(
|
||||
securities.map(security => {
|
||||
return ApplicationSecurity.scan(security);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
} catch (error) {
|
||||
ErrorService.log('runApplicationScan.getApi', error);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -23,4 +23,8 @@ INTERNAL_SMTP_PORT=2525
|
||||
INTERNAL_SMTP_FROM=support@fyipe.com
|
||||
INTERNAL_SMTP_NAME=support
|
||||
FYIPE_HOST=localhost:3002
|
||||
BACKEND_PROTOCOL=http
|
||||
BACKEND_PROTOCOL=http
|
||||
SCRIPT_RUNNER_URL=http://localhost:3009
|
||||
SLACK_ERROR_LOG_WEBHOOK=https://hooks.slack.com/services/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
|
||||
SLACK_ERROR_LOG_CHANNEL=fyipe-logs
|
||||
PORT=3002
|
||||
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
@@ -24,6 +24,5 @@ apiTest.rest
|
||||
application_security_dir
|
||||
container_security_dir
|
||||
|
||||
/greenlock.d
|
||||
/greenlock.d/config.json
|
||||
/greenlock.d/config.json.bak
|
||||
@@ -1 +1 @@
|
||||
{"configDir":"./greenlock.d/","manager":"fyipe-gl-manager"}
|
||||
{"manager":"fyipe-gl-manager","configDir":"./greenlock.d"}
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:15
|
||||
FROM node:16
|
||||
|
||||
#SET ENV Variables
|
||||
ENV PRODUCTION=true
|
||||
|
||||
33
backend/Dockerfile.dev
Normal file
33
backend/Dockerfile.dev
Normal file
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# Fyipe-backend Dockerfile
|
||||
#
|
||||
|
||||
# Pull base image nodejs image.
|
||||
FROM node:16
|
||||
|
||||
#SET ENV Variables
|
||||
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install trivy for container scanning
|
||||
RUN curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/master/contrib/install.sh | sh -s -- -b /usr/local/bin
|
||||
|
||||
# Install app dependencies
|
||||
RUN cd /usr/src/app
|
||||
|
||||
RUN mkdir -p greenlock.d
|
||||
# Copy package.json files
|
||||
COPY ./package.json /usr/src/app/package.json
|
||||
COPY ./package-lock.json /usr/src/app/package-lock.json
|
||||
|
||||
|
||||
RUN npm ci
|
||||
|
||||
# Expose ports.
|
||||
# - 3002: Fyipe-backend
|
||||
EXPOSE 3002
|
||||
EXPOSE 9229
|
||||
|
||||
#Run the app
|
||||
CMD [ "npm", "run", "dev"]
|
||||
@@ -87,17 +87,22 @@ router.get(
|
||||
projectId,
|
||||
idNumber,
|
||||
});
|
||||
incidentId = incidentId._id;
|
||||
const skip = req.query.skip || 0;
|
||||
const limit = req.query.limit || 10;
|
||||
const alerts = await alertService.findBy({
|
||||
query: { incidentId: incidentId },
|
||||
skip,
|
||||
limit,
|
||||
});
|
||||
const count = await alertService.countBy({
|
||||
incidentId: incidentId,
|
||||
});
|
||||
|
||||
let alerts = [],
|
||||
count = 0;
|
||||
if (incidentId) {
|
||||
incidentId = incidentId._id;
|
||||
alerts = await alertService.findBy({
|
||||
query: { incidentId: incidentId },
|
||||
skip,
|
||||
limit,
|
||||
});
|
||||
count = await alertService.countBy({
|
||||
incidentId: incidentId,
|
||||
});
|
||||
}
|
||||
return sendListResponse(req, res, alerts, count);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
|
||||
53
backend/backend/api/applicationScanner.js
Normal file
53
backend/backend/api/applicationScanner.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
*
|
||||
* Copyright HackerBay, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const ApplicationScannerService = require('../services/applicationScannerService');
|
||||
const ApplicationSecurityService = require('../services/applicationSecurityService');
|
||||
const router = express.Router();
|
||||
const isAuthorizedApplicationScanner = require('../middlewares/applicationScannerAuthorization')
|
||||
.isAuthorizedApplicationScanner;
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const sendItemResponse = require('../middlewares/response').sendItemResponse;
|
||||
const getUser = require('../middlewares/user').getUser;
|
||||
const multer = require('multer');
|
||||
const storage = require('../middlewares/upload');
|
||||
|
||||
// Route
|
||||
// Description: Updating profile setting.
|
||||
// Params:
|
||||
// Param 1: req.headers-> {authorization}; req.user-> {id}; req.files-> {profilePic};
|
||||
// Returns: 200: Success, 400: Error; 500: Server Error.
|
||||
|
||||
|
||||
router.get('/applicationSecurities', isAuthorizedApplicationScanner, async function (
|
||||
req,
|
||||
res
|
||||
) {
|
||||
try {
|
||||
const response = await ApplicationSecurityService.getSecuritiesToScan();
|
||||
return sendItemResponse(req, res, response);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/scan/git', isAuthorizedApplicationScanner, async function (req, res) {
|
||||
try {
|
||||
let { security } = req.body;
|
||||
|
||||
security = await ApplicationSecurityService.decryptPassword(security);
|
||||
const securityLog = await ApplicationScannerService.scanApplicationSecurity(
|
||||
security
|
||||
);
|
||||
global.io.emit(`securityLog_${security._id}`, securityLog);
|
||||
return sendItemResponse(req, res, securityLog);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -4,7 +4,7 @@ const { isAuthorized } = require('../middlewares/authorization');
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const sendItemResponse = require('../middlewares/response').sendItemResponse;
|
||||
const ApplicationSecurityService = require('../services/applicationSecurityService');
|
||||
const ProbeService = require('../services/probeService');
|
||||
const ApplicationScannerService = require('../services/applicationScannerService');
|
||||
const RealTimeService = require('../services/realTimeService');
|
||||
const ResourceCategoryService = require('../services/resourceCategoryService');
|
||||
|
||||
@@ -311,10 +311,9 @@ router.post(
|
||||
applicationSecurity
|
||||
);
|
||||
|
||||
const securityLog = await ProbeService.scanApplicationSecurity(
|
||||
const securityLog = await ApplicationScannerService.scanApplicationSecurity(
|
||||
applicationSecurity
|
||||
);
|
||||
|
||||
global.io.emit(
|
||||
`securityLog_${applicationSecurity._id}`,
|
||||
securityLog
|
||||
|
||||
270
backend/backend/api/automatedScript.js
Normal file
270
backend/backend/api/automatedScript.js
Normal file
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
*
|
||||
* Copyright HackerBay, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const AutomatedScriptService = require('../services/automatedScriptService');
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const sendListResponse = require('../middlewares/response').sendListResponse;
|
||||
const { sendItemResponse } = require('../middlewares/response');
|
||||
const { isAuthorized } = require('../middlewares/authorization');
|
||||
const { getUser } = require('../middlewares/user');
|
||||
|
||||
router.get('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
try {
|
||||
const { projectId } = req.params;
|
||||
const { skip, limit } = req.query;
|
||||
const scripts = await AutomatedScriptService.findBy(
|
||||
{ projectId },
|
||||
skip,
|
||||
limit
|
||||
);
|
||||
const count = await AutomatedScriptService.countBy({ projectId });
|
||||
return sendListResponse(req, res, scripts, count);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/:projectId/:automatedSlug',
|
||||
getUser,
|
||||
isAuthorized,
|
||||
async (req, res) => {
|
||||
try {
|
||||
const { automatedSlug } = req.params;
|
||||
const { skip, limit } = req.query;
|
||||
const details = await AutomatedScriptService.findOneBy({
|
||||
slug: automatedSlug,
|
||||
});
|
||||
const logs = await AutomatedScriptService.getAutomatedLogs(
|
||||
{
|
||||
automationScriptId: details._id,
|
||||
},
|
||||
skip,
|
||||
limit
|
||||
);
|
||||
|
||||
if (details.successEvent.length > 0) {
|
||||
details.successEvent = formatEvent(details.successEvent);
|
||||
}
|
||||
|
||||
if (details.failureEvent.length > 0) {
|
||||
details.failureEvent = formatEvent(details.failureEvent);
|
||||
}
|
||||
|
||||
const count = await AutomatedScriptService.countLogsBy({
|
||||
automationScriptId: details._id,
|
||||
});
|
||||
const response = {
|
||||
details,
|
||||
logs,
|
||||
};
|
||||
|
||||
return sendListResponse(req, res, response, count);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Route Description: Creates a new script
|
||||
// req.body -> {name, scriptType, script, successEvent, failureEvent}
|
||||
// Returns: response new script created
|
||||
router.post('/:projectId', getUser, isAuthorized, async (req, res) => {
|
||||
try {
|
||||
const data = req.body;
|
||||
data.projectId = req.params.projectId;
|
||||
if (!data) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Values should not be null',
|
||||
});
|
||||
}
|
||||
if (!data.name || !data.name.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Script name is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.scriptType || data.scriptType.trim().length === 0) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Script Type is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.script || data.script.trim().length === 0) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Script is required',
|
||||
});
|
||||
}
|
||||
|
||||
// check if name already exists
|
||||
const uniqueName = await AutomatedScriptService.findOneBy({
|
||||
projectId: data.projectId,
|
||||
name: data.name,
|
||||
});
|
||||
|
||||
if (uniqueName) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Script name already exists',
|
||||
});
|
||||
}
|
||||
|
||||
if (data.successEvent.length > 0) {
|
||||
data.successEvent = formatEvent(data.successEvent, true);
|
||||
}
|
||||
if (data.failureEvent.length > 0) {
|
||||
data.failureEvent = formatEvent(data.failureEvent, true);
|
||||
}
|
||||
const response = await AutomatedScriptService.createScript(data);
|
||||
return sendItemResponse(req, res, response);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
// Route Description: Update a script
|
||||
// req.body -> {name, scriptType, script, successEvent, failureEvent}
|
||||
// Returns: response script updated
|
||||
router.put(
|
||||
'/:projectId/:automatedScriptId',
|
||||
getUser,
|
||||
isAuthorized,
|
||||
async (req, res) => {
|
||||
try {
|
||||
const automatedScriptId = req.params.automatedScriptId;
|
||||
const data = req.body;
|
||||
data.projectId = req.params.projectId;
|
||||
if (!data) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Values should not be null',
|
||||
});
|
||||
}
|
||||
if (!data.name || !data.name.trim()) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Script name is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.scriptType || data.scriptType.trim().length === 0) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Script Type is required',
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.script || data.script.trim().length === 0) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Script is required',
|
||||
});
|
||||
}
|
||||
|
||||
// check former name
|
||||
const formerName = await AutomatedScriptService.findOneBy({
|
||||
projectId: data.projectId,
|
||||
_id: automatedScriptId,
|
||||
});
|
||||
|
||||
// check if name already exists
|
||||
const uniqueName = await AutomatedScriptService.findOneBy({
|
||||
projectId: data.projectId,
|
||||
name: data.name,
|
||||
});
|
||||
|
||||
if (data.name !== formerName.name && uniqueName) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Script name already exists',
|
||||
});
|
||||
}
|
||||
|
||||
if (data.successEvent.length > 0) {
|
||||
data.successEvent = formatEvent(data.successEvent, true);
|
||||
}
|
||||
if (data.failureEvent.length > 0) {
|
||||
data.failureEvent = formatEvent(data.failureEvent, true);
|
||||
}
|
||||
const response = await AutomatedScriptService.updateOne(
|
||||
{ _id: automatedScriptId },
|
||||
data
|
||||
);
|
||||
return sendItemResponse(req, res, response);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/:projectId/:automatedScriptId/run',
|
||||
getUser,
|
||||
isAuthorized,
|
||||
async (req, res) => {
|
||||
try {
|
||||
const { automatedScriptId } = req.params;
|
||||
const triggeredId = req.user ? req.user.id : null;
|
||||
const response = await AutomatedScriptService.runResource({
|
||||
triggeredId,
|
||||
triggeredBy: 'user',
|
||||
resources: { automatedScript: automatedScriptId },
|
||||
});
|
||||
return sendItemResponse(req, res, response);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
router.delete(
|
||||
'/:projectId/:automatedSlug',
|
||||
getUser,
|
||||
isAuthorized,
|
||||
async function(req, res) {
|
||||
try {
|
||||
const { automatedSlug } = req.params;
|
||||
const userId = req.user ? req.user.id : null;
|
||||
const response = await AutomatedScriptService.deleteBy(
|
||||
{
|
||||
slug: automatedSlug,
|
||||
},
|
||||
userId
|
||||
);
|
||||
return sendItemResponse(req, res, response);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const formatEvent = (arr, type) => {
|
||||
const result = [];
|
||||
for (const item of arr) {
|
||||
if (type) {
|
||||
result.push({ [item.type]: item.resource });
|
||||
} else {
|
||||
for (const [key, value] of Object.entries(item)) {
|
||||
if (key !== '_id') {
|
||||
result.push({
|
||||
type: String(key),
|
||||
resource: String(value),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports = router;
|
||||
60
backend/backend/api/defaultManager.js
Normal file
60
backend/backend/api/defaultManager.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const express = require('express');
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const sendItemResponse = require('../middlewares/response').sendItemResponse;
|
||||
const DefaultManagerService = require('../services/defaultManagerService');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// store default details to the db
|
||||
router.put('/default', async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
store,
|
||||
challenges,
|
||||
renewOffset,
|
||||
renewStagger,
|
||||
accountKeyType,
|
||||
serverKeyType,
|
||||
subscriberEmail,
|
||||
agreeToTerms,
|
||||
} = req.body;
|
||||
|
||||
if (!subscriberEmail) {
|
||||
return sendItemResponse(req, res, {});
|
||||
}
|
||||
|
||||
const data = {};
|
||||
if (store) data.store = store;
|
||||
if (challenges) data.challenges = challenges;
|
||||
if (renewOffset) data.renewOffset = renewOffset;
|
||||
if (renewStagger) data.renewStagger = renewStagger;
|
||||
if (accountKeyType) data.accountKeyType = accountKeyType;
|
||||
if (serverKeyType) data.serverKeyType = serverKeyType;
|
||||
if (subscriberEmail) data.subscriberEmail = subscriberEmail;
|
||||
if (agreeToTerms) data.agreeToTerms = agreeToTerms;
|
||||
|
||||
// if there's no default value
|
||||
// create a default value
|
||||
const defaultManager = await DefaultManagerService.updateOneBy(
|
||||
{ subscriberEmail },
|
||||
data
|
||||
);
|
||||
return sendItemResponse(req, res, defaultManager);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/default', async (req, res) => {
|
||||
try {
|
||||
const defaultManager = await DefaultManagerService.findOneBy({
|
||||
deleted: false,
|
||||
});
|
||||
|
||||
return sendItemResponse(req, res, defaultManager);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -29,9 +29,17 @@ router.put(
|
||||
verificationToken
|
||||
);
|
||||
|
||||
if (!doesTxtRecordExist) {
|
||||
const { result, txtRecords } = doesTxtRecordExist;
|
||||
|
||||
if (!result) {
|
||||
const records =
|
||||
txtRecords.length > 1
|
||||
? txtRecords.join(', ')
|
||||
: txtRecords[0];
|
||||
return sendErrorResponse(req, res, {
|
||||
message: 'TXT record not found',
|
||||
message: `Please specify ${verificationToken} in your DNS. Looks like your current ${
|
||||
txtRecords.length > 1 ? 'records are' : 'record is'
|
||||
} ${records}`,
|
||||
code: 400,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,107 +36,107 @@ const Services = require('../utils/services');
|
||||
// Param 1: req.headers-> {authorization}; req.user-> {id}; req.body-> {monitorId, projectId}
|
||||
// Returns: 200: Incident, 400: Error; 500: Server Error.
|
||||
|
||||
router.post('/:projectId/:monitorId', getUser, isAuthorized, async function(
|
||||
req,
|
||||
res
|
||||
) {
|
||||
try {
|
||||
const monitorId = req.params.monitorId;
|
||||
const projectId = req.params.projectId;
|
||||
const incidentType = req.body.incidentType;
|
||||
const incidentPriority = req.body.incidentPriority;
|
||||
const title = req.body.title;
|
||||
const description = req.body.description;
|
||||
const customFields = req.body.customFields;
|
||||
const userId = req.user ? req.user.id : null;
|
||||
let oldIncidentsCount = null;
|
||||
router.post(
|
||||
'/:projectId/create-incident',
|
||||
getUser,
|
||||
isAuthorized,
|
||||
async function(req, res) {
|
||||
try {
|
||||
const projectId = req.params.projectId;
|
||||
const incidentType = req.body.incidentType;
|
||||
const incidentPriority = req.body.incidentPriority;
|
||||
const title = req.body.title;
|
||||
const description = req.body.description;
|
||||
const customFields = req.body.customFields;
|
||||
const monitors = req.body.monitors;
|
||||
const userId = req.user ? req.user.id : null;
|
||||
let oldIncidentsCount = null;
|
||||
|
||||
if (!monitorId) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Monitor ID must be present.',
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof monitorId !== 'string') {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Monitor ID is not in string type.',
|
||||
});
|
||||
}
|
||||
|
||||
if (!projectId) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Project ID must be present.',
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof projectId !== 'string') {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Project ID is not in string type.',
|
||||
});
|
||||
}
|
||||
|
||||
if (!title) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Title must be present.',
|
||||
});
|
||||
}
|
||||
|
||||
if (incidentType) {
|
||||
if (!['offline', 'online', 'degraded'].includes(incidentType)) {
|
||||
// monitors should be an array containing id of monitor(s)
|
||||
if (monitors && !Array.isArray(monitors)) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Invalid incident type.',
|
||||
message: 'Monitors is not of type array',
|
||||
});
|
||||
}
|
||||
oldIncidentsCount = await IncidentService.countBy({
|
||||
projectId,
|
||||
monitorId,
|
||||
incidentType,
|
||||
resolved: false,
|
||||
deleted: false,
|
||||
manuallyCreated: true,
|
||||
});
|
||||
} else {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'IncidentType must be present.',
|
||||
});
|
||||
}
|
||||
|
||||
if (oldIncidentsCount && oldIncidentsCount > 0) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: `An unresolved incident of type ${incidentType} already exists.`,
|
||||
if (!projectId) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Project ID must be present.',
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof projectId !== 'string') {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Project ID is not in string type.',
|
||||
});
|
||||
}
|
||||
|
||||
if (!title) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Title must be present.',
|
||||
});
|
||||
}
|
||||
|
||||
if (incidentType) {
|
||||
if (!['offline', 'online', 'degraded'].includes(incidentType)) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Invalid incident type.',
|
||||
});
|
||||
}
|
||||
oldIncidentsCount = await IncidentService.countBy({
|
||||
projectId,
|
||||
incidentType,
|
||||
resolved: false,
|
||||
deleted: false,
|
||||
manuallyCreated: true,
|
||||
'monitors.monitorId': { $in: monitors },
|
||||
});
|
||||
} else {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'IncidentType must be present.',
|
||||
});
|
||||
}
|
||||
|
||||
if (oldIncidentsCount && oldIncidentsCount > 0) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: `An unresolved incident of type ${incidentType} already exists.`,
|
||||
});
|
||||
}
|
||||
// Call the IncidentService
|
||||
const incident = await IncidentService.create({
|
||||
projectId,
|
||||
createdById: userId,
|
||||
manuallyCreated: true,
|
||||
incidentType,
|
||||
title,
|
||||
description,
|
||||
incidentPriority,
|
||||
customFields,
|
||||
monitors,
|
||||
});
|
||||
if (incident) {
|
||||
for (const monitor of monitors) {
|
||||
await MonitorStatusService.create({
|
||||
monitorId: monitor,
|
||||
incidentId: incident._id,
|
||||
manuallyCreated: true,
|
||||
status: incidentType,
|
||||
});
|
||||
}
|
||||
}
|
||||
return sendItemResponse(req, res, incident);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
// Call the IncidentService
|
||||
const incident = await IncidentService.create({
|
||||
projectId,
|
||||
monitorId,
|
||||
createdById: userId,
|
||||
manuallyCreated: true,
|
||||
incidentType,
|
||||
title,
|
||||
description,
|
||||
incidentPriority,
|
||||
customFields,
|
||||
});
|
||||
await MonitorStatusService.create({
|
||||
monitorId,
|
||||
incidentId: incident._id,
|
||||
manuallyCreated: true,
|
||||
status: incidentType,
|
||||
});
|
||||
return sendItemResponse(req, res, incident);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// Route
|
||||
// Description: Getting all the incidents by monitor Id.
|
||||
@@ -152,7 +152,7 @@ router.post(
|
||||
try {
|
||||
const { startDate, endDate } = req.body;
|
||||
let query = {
|
||||
monitorId: req.params.monitorId,
|
||||
'monitors.monitorId': { $in: [req.params.monitorId] },
|
||||
projectId: req.params.projectId,
|
||||
};
|
||||
|
||||
@@ -160,7 +160,7 @@ router.post(
|
||||
const start = moment(startDate).toDate();
|
||||
const end = moment(endDate).toDate();
|
||||
query = {
|
||||
monitorId: req.params.monitorId,
|
||||
'monitors.monitorId': { $in: [req.params.monitorId] },
|
||||
projectId: req.params.projectId,
|
||||
createdAt: { $gte: start, $lte: end },
|
||||
};
|
||||
@@ -316,14 +316,14 @@ router.get(
|
||||
isAuthorized,
|
||||
async function(req, res) {
|
||||
try {
|
||||
const { projectId, incidentId } = req.params;
|
||||
const { incidentId } = req.params;
|
||||
|
||||
const incident = await IncidentService.findOneBy({
|
||||
projectId,
|
||||
idNumber: incidentId,
|
||||
});
|
||||
// const incident = await IncidentService.findOneBy({
|
||||
// projectId,
|
||||
// idNumber: incidentId,
|
||||
// });
|
||||
const timeline = await IncidentTimelineService.findBy(
|
||||
{ incidentId: incident._id },
|
||||
{ incidentId },
|
||||
req.query.skip || 0,
|
||||
req.query.limit || 10
|
||||
);
|
||||
@@ -347,9 +347,11 @@ router.get(
|
||||
: null;
|
||||
// Call the IncidentService.
|
||||
const userId = req.user ? req.user.id : null;
|
||||
const { isHome } = req.query;
|
||||
const incident = await IncidentService.getUnresolvedIncidents(
|
||||
subProjectIds,
|
||||
userId
|
||||
userId,
|
||||
isHome
|
||||
);
|
||||
return sendItemResponse(req, res, incident);
|
||||
} catch (error) {
|
||||
@@ -655,6 +657,9 @@ router.post(
|
||||
// handle creation or updating
|
||||
if (!data.id) {
|
||||
data.createdById = req.user.id;
|
||||
data.monitors = incident.monitors.map(
|
||||
monitor => monitor.monitorId
|
||||
);
|
||||
incidentMessage = await IncidentMessageService.create(data);
|
||||
if (data.post_statuspage) {
|
||||
AlertService.sendInvestigationNoteToSubscribers(
|
||||
@@ -810,15 +815,19 @@ router.get(
|
||||
projectId,
|
||||
idNumber: incidentId,
|
||||
});
|
||||
const {
|
||||
statusPages,
|
||||
count,
|
||||
} = await StatusPageService.getStatusPagesForIncident(
|
||||
incident._id,
|
||||
parseInt(req.query.skip) || 0,
|
||||
parseInt(req.query.limit) || 10
|
||||
);
|
||||
return sendListResponse(req, res, statusPages, count);
|
||||
if (incident) {
|
||||
const {
|
||||
statusPages,
|
||||
count,
|
||||
} = await StatusPageService.getStatusPagesForIncident(
|
||||
incident._id,
|
||||
parseInt(req.query.skip) || 0,
|
||||
parseInt(req.query.limit) || 10
|
||||
);
|
||||
return sendListResponse(req, res, statusPages, count);
|
||||
} else {
|
||||
return sendListResponse(req, res, [], 0);
|
||||
}
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
@@ -934,75 +943,82 @@ router.get(
|
||||
type = 'internal';
|
||||
}
|
||||
try {
|
||||
let incidentMessages, result;
|
||||
let incidentMessages,
|
||||
result = [],
|
||||
count = 0;
|
||||
const idNumber = req.params.incidentId;
|
||||
const projectId = req.params.projectId;
|
||||
let incidentId = await IncidentService.findOneBy({
|
||||
projectId,
|
||||
idNumber,
|
||||
});
|
||||
incidentId = incidentId._id;
|
||||
if (type === 'investigation') {
|
||||
incidentMessages = await IncidentMessageService.findBy(
|
||||
{ incidentId, type },
|
||||
req.query.skip || 0,
|
||||
req.query.limit || 10
|
||||
);
|
||||
} else {
|
||||
incidentMessages = await IncidentMessageService.findBy({
|
||||
if (incidentId) {
|
||||
incidentId = incidentId._id;
|
||||
if (type === 'investigation') {
|
||||
incidentMessages = await IncidentMessageService.findBy(
|
||||
{ incidentId, type },
|
||||
req.query.skip || 0,
|
||||
req.query.limit || 10
|
||||
);
|
||||
} else {
|
||||
incidentMessages = await IncidentMessageService.findBy({
|
||||
incidentId,
|
||||
type,
|
||||
});
|
||||
}
|
||||
const timeline = await IncidentTimelineService.findBy({
|
||||
incidentId,
|
||||
});
|
||||
const alerts = await AlertService.findBy({
|
||||
query: { incidentId: incidentId },
|
||||
});
|
||||
const subscriberAlerts = await subscriberAlertService.findBy({
|
||||
incidentId: incidentId,
|
||||
projectId,
|
||||
});
|
||||
count = await IncidentMessageService.countBy({
|
||||
incidentId,
|
||||
type,
|
||||
});
|
||||
}
|
||||
const timeline = await IncidentTimelineService.findBy({
|
||||
incidentId,
|
||||
});
|
||||
const alerts = await AlertService.findBy({
|
||||
query: { incidentId: incidentId },
|
||||
});
|
||||
const subscriberAlerts = await subscriberAlertService.findBy({
|
||||
incidentId: incidentId,
|
||||
projectId,
|
||||
});
|
||||
const count = await IncidentMessageService.countBy({
|
||||
incidentId,
|
||||
type,
|
||||
});
|
||||
if (type === 'investigation') {
|
||||
result = incidentMessages;
|
||||
} else {
|
||||
const subAlerts = await Services.deduplicate(subscriberAlerts);
|
||||
let callScheduleStatus = await onCallScheduleStatusService.findBy(
|
||||
{
|
||||
query: { incident: incidentId },
|
||||
}
|
||||
);
|
||||
callScheduleStatus = await Services.checkCallSchedule(
|
||||
callScheduleStatus
|
||||
);
|
||||
const timelineAlerts = [
|
||||
...timeline,
|
||||
...alerts,
|
||||
...incidentMessages,
|
||||
].sort((a, b) => {
|
||||
return b.createdAt - a.createdAt;
|
||||
});
|
||||
incidentMessages = [
|
||||
...timelineAlerts,
|
||||
...subAlerts,
|
||||
...callScheduleStatus,
|
||||
];
|
||||
incidentMessages.sort(
|
||||
(a, b) =>
|
||||
typeof a.schedule !== 'object' &&
|
||||
b.createdAt - a.createdAt
|
||||
);
|
||||
const filteredMsg = incidentMessages.filter(
|
||||
a =>
|
||||
a.status !== 'internal notes added' &&
|
||||
a.status !== 'internal notes updated'
|
||||
);
|
||||
result = await Services.rearrangeDuty(filteredMsg);
|
||||
if (type === 'investigation') {
|
||||
result = incidentMessages;
|
||||
} else {
|
||||
const subAlerts = await Services.deduplicate(
|
||||
subscriberAlerts
|
||||
);
|
||||
let callScheduleStatus = await onCallScheduleStatusService.findBy(
|
||||
{
|
||||
query: { incident: incidentId },
|
||||
}
|
||||
);
|
||||
callScheduleStatus = await Services.checkCallSchedule(
|
||||
callScheduleStatus
|
||||
);
|
||||
const timelineAlerts = [
|
||||
...timeline,
|
||||
...alerts,
|
||||
...incidentMessages,
|
||||
].sort((a, b) => {
|
||||
return b.createdAt - a.createdAt;
|
||||
});
|
||||
incidentMessages = [
|
||||
...timelineAlerts,
|
||||
...subAlerts,
|
||||
...callScheduleStatus,
|
||||
];
|
||||
incidentMessages.sort(
|
||||
(a, b) =>
|
||||
typeof a.schedule !== 'object' &&
|
||||
b.createdAt - a.createdAt
|
||||
);
|
||||
const filteredMsg = incidentMessages.filter(
|
||||
a =>
|
||||
a.status !== 'internal notes added' &&
|
||||
a.status !== 'internal notes updated'
|
||||
);
|
||||
|
||||
result = await Services.rearrangeDuty(filteredMsg);
|
||||
}
|
||||
}
|
||||
return sendListResponse(req, res, result, count);
|
||||
} catch (error) {
|
||||
@@ -1065,12 +1081,21 @@ router.get(
|
||||
try {
|
||||
const userId = req.user ? req.user.id : null;
|
||||
await IncidentService.resolve(req.params.incidentId, userId);
|
||||
|
||||
// get incident properties to build url
|
||||
const { incidentId, projectId } = req.params;
|
||||
const incident = await IncidentService.findOneBy({
|
||||
projectId,
|
||||
_id: incidentId,
|
||||
});
|
||||
const { projectId: project } = incident;
|
||||
|
||||
return res.status(200).render('incidentAction.ejs', {
|
||||
title: 'Incident Resolved',
|
||||
title_message: 'Incident Resolved',
|
||||
body_message: 'Your incident is now resolved.',
|
||||
action: 'resolve',
|
||||
dashboard_url: global.dashboardHost,
|
||||
dashboard_url: `${global.dashboardHost}/project/${project.slug}/incidents/${incident.idNumber}`,
|
||||
apiUrl: global.apiHost,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -1097,12 +1122,21 @@ router.get(
|
||||
userId,
|
||||
req.user.name
|
||||
);
|
||||
|
||||
// get incident properties to build url
|
||||
const { incidentId, projectId } = req.params;
|
||||
const incident = await IncidentService.findOneBy({
|
||||
projectId,
|
||||
_id: incidentId,
|
||||
});
|
||||
const { projectId: project } = incident;
|
||||
|
||||
return res.status(200).render('incidentAction.ejs', {
|
||||
title: 'Incident Acknowledged',
|
||||
title_message: 'Incident Acknowledged',
|
||||
body_message: 'Your incident is now acknowledged',
|
||||
body_message: 'Your incident is now acknowledged.',
|
||||
action: 'acknowledge',
|
||||
dashboard_url: global.dashboardHost,
|
||||
dashboard_url: `${global.dashboardHost}/project/${project.slug}/incidents/${incident.idNumber}`,
|
||||
apiUrl: global.apiHost,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,7 +6,6 @@ const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const sendListResponse = require('../middlewares/response').sendListResponse;
|
||||
const sendItemResponse = require('../middlewares/response').sendItemResponse;
|
||||
const IncidentPrioritiesService = require('../services/incidentPrioritiesService');
|
||||
const IncidentSettingsService = require('../services/incidentSettingsService');
|
||||
|
||||
router.get('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
const { projectId } = req.params;
|
||||
@@ -126,17 +125,6 @@ router.delete('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
}
|
||||
|
||||
try {
|
||||
const incidentSettings = await IncidentSettingsService.findOne({
|
||||
projectId,
|
||||
});
|
||||
|
||||
if (`${incidentSettings.incidentPriority}` === `${_id}`)
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message:
|
||||
'This incident priority is marked as default and cannot be deleted.',
|
||||
});
|
||||
|
||||
const IncidentPriority = await IncidentPrioritiesService.deleteBy({
|
||||
projectId,
|
||||
_id,
|
||||
|
||||
@@ -4,6 +4,7 @@ const getUser = require('../middlewares/user').getUser;
|
||||
const { isAuthorized } = require('../middlewares/authorization');
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const sendItemResponse = require('../middlewares/response').sendItemResponse;
|
||||
const sendListResponse = require('../middlewares/response').sendListResponse;
|
||||
const IncidentSettingsService = require('../services/incidentSettingsService');
|
||||
const IncidentPrioritiesService = require('../services/incidentPrioritiesService');
|
||||
const { variables } = require('../config/incidentDefaultSettings');
|
||||
@@ -16,75 +17,107 @@ router.get('/variables', async function(req, res) {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
const { projectId } = req.params;
|
||||
if (!projectId)
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Project Id must be present',
|
||||
});
|
||||
try {
|
||||
const incidentSettings = await IncidentSettingsService.findOne({
|
||||
projectId,
|
||||
});
|
||||
return sendItemResponse(req, res, incidentSettings);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/:projectId/setDefault', getUser, isAuthorized, async function(
|
||||
// fetch default incident template in a project
|
||||
router.get('/:projectId/default', getUser, isAuthorized, async function(
|
||||
req,
|
||||
res
|
||||
) {
|
||||
const { projectId } = req.params;
|
||||
const { incidentPriority } = req.body;
|
||||
if (!projectId)
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Project Id must be present.',
|
||||
});
|
||||
if (!incidentPriority)
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Incident priority must be present.',
|
||||
});
|
||||
|
||||
try {
|
||||
// Update Default Incident Priority
|
||||
const priority = await IncidentPrioritiesService.findOne({
|
||||
_id: incidentPriority,
|
||||
});
|
||||
|
||||
if (!priority) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: "Incident priority doesn't exist.",
|
||||
});
|
||||
const { projectId } = req.params;
|
||||
if (!projectId) {
|
||||
const error = new Error('Project Id must be present');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
const defaultPrioritySetting = await IncidentSettingsService.updateOne(
|
||||
{
|
||||
projectId,
|
||||
},
|
||||
{
|
||||
incidentPriority,
|
||||
}
|
||||
);
|
||||
return sendItemResponse(req, res, defaultPrioritySetting);
|
||||
|
||||
const query = { projectId, isDefault: true };
|
||||
const template = await IncidentSettingsService.findOne(query);
|
||||
|
||||
return sendItemResponse(req, res, template);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
const { projectId } = req.params;
|
||||
const { title, description, incidentPriority } = req.body;
|
||||
// fetch all incident template in a project
|
||||
router.get('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
try {
|
||||
const { projectId } = req.params;
|
||||
const { skip, limit } = req.query;
|
||||
|
||||
if (!projectId) {
|
||||
const error = new Error('Project Id must be present');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const query = { projectId };
|
||||
const templates = await IncidentSettingsService.findBy({
|
||||
query,
|
||||
limit,
|
||||
skip,
|
||||
});
|
||||
const count = await IncidentSettingsService.countBy(query);
|
||||
return sendListResponse(req, res, templates, count);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.put(
|
||||
'/:projectId/:templateId/setDefault',
|
||||
getUser,
|
||||
isAuthorized,
|
||||
async function(req, res) {
|
||||
const { projectId, templateId } = req.params;
|
||||
if (!projectId)
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Project Id must be present.',
|
||||
});
|
||||
|
||||
try {
|
||||
const defaultPrioritySetting = await IncidentSettingsService.updateOne(
|
||||
{
|
||||
_id: templateId,
|
||||
projectId,
|
||||
},
|
||||
{
|
||||
isDefault: true,
|
||||
}
|
||||
);
|
||||
return sendItemResponse(req, res, defaultPrioritySetting);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
router.put('/:projectId/:templateId', getUser, isAuthorized, async function(
|
||||
req,
|
||||
res
|
||||
) {
|
||||
const { projectId, templateId } = req.params;
|
||||
const { title, description, incidentPriority, isDefault, name } = req.body;
|
||||
if (!projectId)
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Project Id must be present.',
|
||||
});
|
||||
|
||||
if (!templateId)
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Incident settings Id must be present.',
|
||||
});
|
||||
|
||||
if (!name) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Name must be present',
|
||||
});
|
||||
}
|
||||
|
||||
if (!title)
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
@@ -112,11 +145,14 @@ router.put('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
const incidentSettings = await IncidentSettingsService.updateOne(
|
||||
{
|
||||
projectId,
|
||||
_id: templateId,
|
||||
},
|
||||
{
|
||||
title,
|
||||
description,
|
||||
incidentPriority,
|
||||
isDefault,
|
||||
name,
|
||||
}
|
||||
);
|
||||
return sendItemResponse(req, res, incidentSettings);
|
||||
@@ -125,4 +161,90 @@ router.put('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/:projectId/:templateId', getUser, isAuthorized, async function(
|
||||
req,
|
||||
res
|
||||
) {
|
||||
try {
|
||||
const { projectId, templateId } = req.params;
|
||||
|
||||
if (!projectId) {
|
||||
const error = new Error('Project Id must be present');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
if (!templateId) {
|
||||
const error = new Error('Incident settings Id must be present.');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const incidentSetting = await IncidentSettingsService.deleteBy({
|
||||
_id: templateId,
|
||||
projectId,
|
||||
});
|
||||
return sendItemResponse(req, res, incidentSetting);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
try {
|
||||
const { projectId } = req.params;
|
||||
// description is optional
|
||||
const {
|
||||
title,
|
||||
description,
|
||||
incidentPriority,
|
||||
isDefault = false,
|
||||
name,
|
||||
} = req.body;
|
||||
|
||||
if (!projectId) {
|
||||
const error = new Error('Project Id must be present');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
if (!name) {
|
||||
const error = new Error('Name must be present');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
if (!title) {
|
||||
const error = new Error('Title must be present');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
if (!incidentPriority) {
|
||||
const error = new Error('Incident priority must be present');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const priority = await IncidentPrioritiesService.findOne({
|
||||
_id: incidentPriority,
|
||||
});
|
||||
if (!priority) {
|
||||
const error = new Error("Incident priority doesn't exist.");
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const data = {
|
||||
projectId,
|
||||
title,
|
||||
description,
|
||||
incidentPriority,
|
||||
isDefault,
|
||||
name,
|
||||
};
|
||||
const incidentSetting = await IncidentSettingsService.create(data);
|
||||
|
||||
return sendItemResponse(req, res, incidentSetting);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -30,6 +30,7 @@ router.post('/', async function(req, res) {
|
||||
data.country = body.country;
|
||||
data.message = body.message || null;
|
||||
data.whitepaperName = body.whitepaper_name || null;
|
||||
data.source = JSON.parse(body.source) || null;
|
||||
const lead = await LeadService.create(data);
|
||||
return sendItemResponse(req, res, lead);
|
||||
} catch (error) {
|
||||
|
||||
@@ -925,4 +925,25 @@ router.post(
|
||||
}
|
||||
);
|
||||
|
||||
// api to calculate time for monitorInfo (status page)
|
||||
router.post('/:monitorId/calculate-time', async function(req, res) {
|
||||
try {
|
||||
const { monitorId } = req.params;
|
||||
const { statuses, start, range } = req.body;
|
||||
|
||||
const monitor = await MonitorService.findOneBy({ _id: monitorId });
|
||||
if (!monitor) {
|
||||
const error = new Error('Monitor not found or does not exist');
|
||||
error.code = 400;
|
||||
throw error;
|
||||
}
|
||||
const result = await MonitorService.calcTime(statuses, start, range);
|
||||
result.monitorId = monitor._id;
|
||||
|
||||
return sendItemResponse(req, res, result);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -37,30 +37,28 @@ router.get('/:projectId', getUser, isAuthorized, getSubProjects, async function(
|
||||
}
|
||||
});
|
||||
|
||||
router.put(
|
||||
'/:projectId/:notificationId/read',
|
||||
getUser,
|
||||
isAuthorized,
|
||||
async function(req, res) {
|
||||
try {
|
||||
const notificationId = req.params.notificationId;
|
||||
const userId = req.user ? req.user.id : null;
|
||||
router.put('/:projectId/read', getUser, isAuthorized, async function(req, res) {
|
||||
try {
|
||||
// const notificationId = req.params.notificationId;
|
||||
const userId = req.user ? req.user.id : null;
|
||||
|
||||
const { notificationIds } = req.body;
|
||||
const notifications = [];
|
||||
for (const notificationId of notificationIds) {
|
||||
const notification = await NotificationService.updateOneBy(
|
||||
{ _id: notificationId },
|
||||
{ read: [userId] }
|
||||
);
|
||||
if (notification) {
|
||||
return sendItemResponse(req, res, notification);
|
||||
} else {
|
||||
const error = new Error('Notification not found.');
|
||||
error.code = 400;
|
||||
return sendErrorResponse(req, res, error);
|
||||
notifications.push(notificationId);
|
||||
}
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
|
||||
return sendItemResponse(req, res, notifications);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
router.put(
|
||||
'/:projectId/:notificationId/closed',
|
||||
|
||||
@@ -9,7 +9,6 @@ const ProbeService = require('../services/probeService');
|
||||
const MonitorService = require('../services/monitorService');
|
||||
const ProjectService = require('../services/projectService');
|
||||
const LighthouseLogService = require('../services/lighthouseLogService');
|
||||
const ApplicationSecurityService = require('../services/applicationSecurityService');
|
||||
const ContainerSecurityService = require('../services/containerSecurityService');
|
||||
const router = express.Router();
|
||||
const isAuthorizedAdmin = require('../middlewares/clusterAuthorization')
|
||||
@@ -75,39 +74,6 @@ router.delete('/:id', getUser, isAuthorizedAdmin, async function(req, res) {
|
||||
// Params:
|
||||
// Param 1: req.headers-> {authorization}; req.user-> {id}; req.files-> {profilePic};
|
||||
// Returns: 200: Success, 400: Error; 500: Server Error.
|
||||
router.put('/update/image', getUser, async function(req, res) {
|
||||
try {
|
||||
const upload = multer({
|
||||
storage,
|
||||
}).fields([
|
||||
{
|
||||
name: 'probeImage',
|
||||
maxCount: 1,
|
||||
},
|
||||
]);
|
||||
upload(req, res, async function(error) {
|
||||
const probeId = req.body.id;
|
||||
const data = req.body;
|
||||
|
||||
if (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
if (
|
||||
req.files &&
|
||||
req.files.probeImage &&
|
||||
req.files.probeImage[0].filename
|
||||
) {
|
||||
data.probeImage = req.files.probeImage[0].filename;
|
||||
}
|
||||
|
||||
// Call the ProbeService
|
||||
const save = await ProbeService.updateOneBy({ _id: probeId }, data);
|
||||
return sendItemResponse(req, res, save);
|
||||
});
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/monitors', isAuthorizedProbe, async function(req, res) {
|
||||
try {
|
||||
@@ -328,27 +294,8 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
failedReasons: upFailedReasons,
|
||||
matchedCriterion: matchedUpCriterion,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.up
|
||||
? ProbeService.scriptConditions(
|
||||
res,
|
||||
resp,
|
||||
monitor.criteria.up
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
|
||||
const {
|
||||
stat: validDegraded,
|
||||
successReasons: degradedSuccessReasons,
|
||||
failedReasons: degradedFailedReasons,
|
||||
matchedUpCriterion: matchedDegradedCriterion,
|
||||
} = await (monitor &&
|
||||
monitor.criteria &&
|
||||
monitor.criteria.degraded
|
||||
? ProbeService.scriptConditions(
|
||||
res,
|
||||
resp,
|
||||
monitor.criteria.degraded
|
||||
)
|
||||
: { stat: false, reasons: [] });
|
||||
? ProbeService.scriptConditions(resp, monitor.criteria.up)
|
||||
: { stat: false, successReasons: [], failedReasons: [] });
|
||||
|
||||
const {
|
||||
stat: validDown,
|
||||
@@ -356,38 +303,49 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
failedReasons: downFailedReasons,
|
||||
matchedCriterion: matchedDownCriterion,
|
||||
} = await (monitor && monitor.criteria && monitor.criteria.down
|
||||
? ProbeService.scriptConditions(res, resp, [
|
||||
? ProbeService.scriptConditions(resp, [
|
||||
...monitor.criteria.down.filter(
|
||||
criterion => criterion.default !== true
|
||||
),
|
||||
])
|
||||
: { stat: false, reasons: [] });
|
||||
: { stat: false, successReasons: [], failedReasons: [] });
|
||||
|
||||
const {
|
||||
stat: validDegraded,
|
||||
successReasons: degradedSuccessReasons,
|
||||
failedReasons: degradedFailedReasons,
|
||||
matchedCriterion: matchedDegradedCriterion,
|
||||
} = await (monitor &&
|
||||
monitor.criteria &&
|
||||
monitor.criteria.degraded
|
||||
? ProbeService.scriptConditions(
|
||||
resp,
|
||||
monitor.criteria.degraded
|
||||
)
|
||||
: { stat: false, successReasons: [], failedReasons: [] });
|
||||
|
||||
if (validUp) {
|
||||
data.status = 'online';
|
||||
data.reason = upSuccessReasons;
|
||||
status = 'online';
|
||||
reason = upSuccessReasons;
|
||||
matchedCriterion = matchedUpCriterion;
|
||||
} else if (validDown) {
|
||||
status = 'offline';
|
||||
reason = [...downSuccessReasons, ...upFailedReasons];
|
||||
matchedCriterion = matchedDownCriterion;
|
||||
} else if (validDegraded) {
|
||||
data.status = 'degraded';
|
||||
data.reason = [
|
||||
status = 'degraded';
|
||||
reason = [
|
||||
...degradedSuccessReasons,
|
||||
...upFailedReasons,
|
||||
...downFailedReasons,
|
||||
];
|
||||
matchedCriterion = matchedDegradedCriterion;
|
||||
} else if (validDown) {
|
||||
data.status = 'offline';
|
||||
data.reason = [
|
||||
...downSuccessReasons,
|
||||
...degradedFailedReasons,
|
||||
...upFailedReasons,
|
||||
];
|
||||
matchedCriterion = matchedDownCriterion;
|
||||
} else {
|
||||
data.status = 'offline';
|
||||
data.reason = [
|
||||
status = 'offline';
|
||||
reason = [
|
||||
...downFailedReasons,
|
||||
...degradedFailedReasons,
|
||||
...upFailedReasons,
|
||||
...degradedFailedReasons,
|
||||
];
|
||||
if (monitor.criteria.down) {
|
||||
matchedCriterion = monitor.criteria.down.find(
|
||||
@@ -395,7 +353,7 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
);
|
||||
}
|
||||
}
|
||||
resp.status = null;
|
||||
|
||||
data.status = status;
|
||||
data.reason = reason;
|
||||
}
|
||||
@@ -583,6 +541,15 @@ router.post('/ping/:monitorId', isAuthorizedProbe, async function(
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'script') {
|
||||
data.scriptMetadata = {
|
||||
executionTime: resp.executionTime,
|
||||
consoleLogs: resp.consoleLogs,
|
||||
error: resp.error,
|
||||
statusText: resp.statusText,
|
||||
};
|
||||
}
|
||||
|
||||
data.matchedCriterion = matchedCriterion;
|
||||
// update monitor to save the last matched criterion
|
||||
await MonitorService.updateOneBy(
|
||||
@@ -709,35 +676,7 @@ router.get('/:projectId/probes', getUser, isAuthorized, async function(
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/applicationSecurities', isAuthorizedProbe, async function(
|
||||
req,
|
||||
res
|
||||
) {
|
||||
try {
|
||||
const response = await ApplicationSecurityService.getSecuritiesToScan();
|
||||
return sendItemResponse(req, res, response);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/scan/git', isAuthorizedProbe, async function(req, res) {
|
||||
try {
|
||||
let { security } = req.body;
|
||||
|
||||
security = await ApplicationSecurityService.decryptPassword(security);
|
||||
|
||||
const securityLog = await ProbeService.scanApplicationSecurity(
|
||||
security
|
||||
);
|
||||
global.io.emit(`securityLog_${security._id}`, securityLog);
|
||||
return sendItemResponse(req, res, securityLog);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
router.get('/containerSecurities', isAuthorizedProbe, async function(req, res) {
|
||||
try {
|
||||
|
||||
@@ -9,6 +9,7 @@ const AirtableService = require('../services/airtableService');
|
||||
const getUser = require('../middlewares/user').getUser;
|
||||
const isUserMasterAdmin = require('../middlewares/user').isUserMasterAdmin;
|
||||
const isUserOwner = require('../middlewares/project').isUserOwner;
|
||||
const isUserAdmin = require('../middlewares/project').isUserAdmin;
|
||||
const { IS_SAAS_SERVICE } = require('../config/server');
|
||||
const { isAuthorized } = require('../middlewares/authorization');
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
@@ -256,7 +257,7 @@ router.put(
|
||||
'/:projectId/renameProject',
|
||||
getUser,
|
||||
isAuthorized,
|
||||
isUserOwner,
|
||||
isUserAdmin,
|
||||
async function(req, res) {
|
||||
try {
|
||||
const projectId = req.params.projectId;
|
||||
@@ -856,6 +857,18 @@ router.get('/projects/:slug', getUser, isUserMasterAdmin, async function(
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/project-slug/:slug', getUser, async function(req, res) {
|
||||
try {
|
||||
const { slug } = req.params;
|
||||
const project = await ProjectService.findOneBy({
|
||||
slug,
|
||||
});
|
||||
return sendItemResponse(req, res, project);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
router.put(
|
||||
'/:projectId/blockProject',
|
||||
getUser,
|
||||
|
||||
@@ -10,6 +10,7 @@ const sendItemResponse = require('../middlewares/response').sendItemResponse;
|
||||
const { getSubProjects } = require('../middlewares/subProject');
|
||||
const ScheduledEventNoteService = require('../services/scheduledEventNoteService');
|
||||
const moment = require('moment');
|
||||
const MonitorService = require('../services/monitorService');
|
||||
|
||||
router.post('/:projectId', getUser, isAuthorized, async function(req, res) {
|
||||
try {
|
||||
@@ -294,6 +295,19 @@ router.put('/:projectId/:eventId/cancel', getUser, isAuthorized, async function(
|
||||
});
|
||||
}
|
||||
|
||||
if (fetchEvent && !fetchEvent.monitorDuringEvent) {
|
||||
for (const monitor of fetchEvent.monitors) {
|
||||
await MonitorService.updateOneBy(
|
||||
{
|
||||
_id: monitor.monitorId._id || monitor.monitorId,
|
||||
},
|
||||
{
|
||||
shouldNotMonitor: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const event = await ScheduledEventService.updateBy(
|
||||
{ _id: eventId },
|
||||
{
|
||||
@@ -305,20 +319,22 @@ router.put('/:projectId/:eventId/cancel', getUser, isAuthorized, async function(
|
||||
|
||||
const scheduledEvent = event[0];
|
||||
|
||||
if (scheduledEvent.alertSubscriber) {
|
||||
// handle this asynchronous operation in the background
|
||||
AlertService.sendCancelledScheduledEventToSubscribers(
|
||||
scheduledEvent
|
||||
);
|
||||
}
|
||||
if (scheduledEvent) {
|
||||
if (scheduledEvent.alertSubscriber) {
|
||||
// handle this asynchronous operation in the background
|
||||
AlertService.sendCancelledScheduledEventToSubscribers(
|
||||
scheduledEvent
|
||||
);
|
||||
}
|
||||
|
||||
await ScheduledEventNoteService.create({
|
||||
content: 'THIS SCHEDULED EVENT HAS BEEN CANCELLED',
|
||||
scheduledEventId: scheduledEvent._id,
|
||||
createdById: scheduledEvent.createdById._id,
|
||||
type: 'investigation',
|
||||
event_state: 'Cancelled',
|
||||
});
|
||||
await ScheduledEventNoteService.create({
|
||||
content: 'THIS SCHEDULED EVENT HAS BEEN CANCELLED',
|
||||
scheduledEventId: scheduledEvent._id,
|
||||
createdById: scheduledEvent.createdById._id,
|
||||
type: 'investigation',
|
||||
event_state: 'Cancelled',
|
||||
});
|
||||
}
|
||||
|
||||
return sendItemResponse(req, res, scheduledEvent);
|
||||
} catch (error) {
|
||||
@@ -564,8 +580,12 @@ router.post('/:projectId/:eventId/notes', getUser, isAuthorized, async function(
|
||||
const data = req.body;
|
||||
data.scheduledEventId = eventId;
|
||||
data.createdById = userId;
|
||||
|
||||
if (!data.scheduledEventId) {
|
||||
if (
|
||||
!data.scheduledEventId ||
|
||||
!data.scheduledEventId.trim() ||
|
||||
data.scheduledEventId === undefined ||
|
||||
data.scheduledEventId === 'undefined'
|
||||
) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'Scheduled Event ID is required.',
|
||||
|
||||
@@ -289,7 +289,7 @@ const getIncidents = async (projectIds, val, parentProjectId) => {
|
||||
parentProjectId === String(incident.projectId._id),
|
||||
projectName: incident.projectId.name,
|
||||
componentId: incident.monitorId.componentId.slug,
|
||||
notificationId: incident.notificationId,
|
||||
notifications: incident.notifications,
|
||||
incident: incident,
|
||||
};
|
||||
}),
|
||||
|
||||
120
backend/backend/api/siteManager.js
Normal file
120
backend/backend/api/siteManager.js
Normal file
@@ -0,0 +1,120 @@
|
||||
const express = require('express');
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const sendItemResponse = require('../middlewares/response').sendItemResponse;
|
||||
const SiteManagerService = require('../services/siteManagerService');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// store site details to the db
|
||||
router.post('/site', async (req, res) => {
|
||||
try {
|
||||
const data = req.body;
|
||||
|
||||
const site = await SiteManagerService.create(data);
|
||||
return sendItemResponse(req, res, site);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
// update site details in the db
|
||||
router.put('/site', async (req, res) => {
|
||||
try {
|
||||
const { subject } = req.query;
|
||||
const site = await SiteManagerService.updateOneBy(
|
||||
{ subject },
|
||||
req.body
|
||||
);
|
||||
|
||||
return sendItemResponse(req, res, site);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
// fetch a site detail
|
||||
router.get('/site', async (req, res) => {
|
||||
try {
|
||||
const { servername } = req.query;
|
||||
const site = await SiteManagerService.findOneBy({
|
||||
subject: servername,
|
||||
});
|
||||
|
||||
return sendItemResponse(req, res, site);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
// fetch all sites
|
||||
router.get('/sites', async (req, res) => {
|
||||
try {
|
||||
const sites = await SiteManagerService.findBy({});
|
||||
return sendItemResponse(req, res, sites);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
// fetch all sites by servernames
|
||||
router.post('/site/servernames', async (req, res) => {
|
||||
try {
|
||||
const { servernames = [] } = req.body;
|
||||
const sites = await SiteManagerService.findBy({
|
||||
subject: { $in: servernames },
|
||||
});
|
||||
return sendItemResponse(req, res, sites);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
// fetch sites base on the options
|
||||
router.post('/site/opts', async (req, res) => {
|
||||
try {
|
||||
const { issuedBefore, expiresBefore, renewBefore } = req.body;
|
||||
const query = { $or: [] };
|
||||
|
||||
if (issuedBefore) {
|
||||
query.$or.push({
|
||||
issuedAt: {
|
||||
$lt: issuedBefore,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (expiresBefore) {
|
||||
query.$or.push({
|
||||
expiresAt: {
|
||||
$lt: expiresBefore,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (renewBefore) {
|
||||
query.$or.push({
|
||||
renewAt: {
|
||||
$lt: renewBefore,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
query.deleted = false;
|
||||
const sites = await SiteManagerService.findBy(query);
|
||||
return sendItemResponse(req, res, sites);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
// delete an site detail
|
||||
router.delete('/site', async (req, res) => {
|
||||
try {
|
||||
const { subject } = req.query;
|
||||
|
||||
const site = await SiteManagerService.deleteBy({ subject });
|
||||
return sendItemResponse(req, res, site);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -37,6 +37,9 @@ router.get('/challenge/authorization/:token', async (req, res) => {
|
||||
try {
|
||||
const { token } = req.params;
|
||||
const acmeChallenge = await SslService.findOneBy({ token });
|
||||
if (!acmeChallenge) {
|
||||
return sendItemResponse(req, res, '');
|
||||
}
|
||||
return sendItemResponse(req, res, acmeChallenge.keyAuthorization);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
|
||||
@@ -327,12 +327,14 @@ router.get('/tlsCredential', async function(req, res) {
|
||||
user
|
||||
);
|
||||
|
||||
let domainObj;
|
||||
statusPage.domains.forEach(eachDomain => {
|
||||
if (eachDomain.domain === domain) {
|
||||
domainObj = eachDomain;
|
||||
}
|
||||
});
|
||||
let domainObj = {};
|
||||
statusPage &&
|
||||
statusPage.domains &&
|
||||
statusPage.domains.forEach(eachDomain => {
|
||||
if (eachDomain.domain === domain) {
|
||||
domainObj = eachDomain;
|
||||
}
|
||||
});
|
||||
|
||||
return sendItemResponse(req, res, {
|
||||
cert: domainObj.cert,
|
||||
@@ -705,10 +707,9 @@ router.get('/:statusPageId/rss', checkUser, async function(req, res) {
|
||||
pubDate: new Date(incident.createdAt).toUTCString(),
|
||||
description: `<![CDATA[Description: ${
|
||||
incident.description
|
||||
}<br>Incident Id: ${incident._id.toString()} <br>Monitor's Name: ${
|
||||
incident.monitorId.name
|
||||
}
|
||||
<br>Monitor's Id: ${incident.monitorId._id.toString()} <br>Acknowledge Time: ${
|
||||
}<br>Incident Id: ${incident._id.toString()} <br>Monitor Name(s): ${handleMonitorList(
|
||||
incident.monitors
|
||||
)}<br>Acknowledge Time: ${
|
||||
incident.acknowledgedAt
|
||||
}<br>Resolve Time: ${incident.resolvedAt}<br>${
|
||||
incident.investigationNote
|
||||
@@ -768,8 +769,10 @@ router.get(
|
||||
let result;
|
||||
const statusPageSlug = req.params.statusPageSlug;
|
||||
const skip = req.query.skip || 0;
|
||||
const limit = req.query.limit || 5;
|
||||
const limit = req.query.limit || 10;
|
||||
const days = req.query.days || 14;
|
||||
const newTheme = req.query.newTheme;
|
||||
|
||||
try {
|
||||
// Call the StatusPageService.
|
||||
const response = await StatusPageService.getNotes(
|
||||
@@ -780,7 +783,7 @@ router.get(
|
||||
const notes = response.notes;
|
||||
const count = response.count;
|
||||
const updatedNotes = [];
|
||||
if (parseInt(limit) === 15) {
|
||||
if (newTheme) {
|
||||
if (notes.length > 0) {
|
||||
for (const note of notes) {
|
||||
const statusPageNote = await StatusPageService.getIncidentNotes(
|
||||
@@ -792,7 +795,7 @@ router.get(
|
||||
const sortMsg = statusPageNote.message.reverse();
|
||||
|
||||
updatedNotes.push({
|
||||
...note._doc,
|
||||
...note,
|
||||
message: sortMsg,
|
||||
});
|
||||
}
|
||||
@@ -882,7 +885,7 @@ router.get('/:projectId/:monitorId/individualnotes', checkUser, async function(
|
||||
const skip = req.query.skip || 0;
|
||||
const limit = req.query.limit || 5;
|
||||
const query = {
|
||||
monitorId: req.params.monitorId,
|
||||
'monitors.monitorId': req.params.monitorId,
|
||||
deleted: false,
|
||||
createdAt: { $gte: start, $lt: end },
|
||||
};
|
||||
@@ -908,7 +911,7 @@ router.get('/:projectId/:monitorId/individualnotes', checkUser, async function(
|
||||
const sortMsg = statusPageNote.message.reverse();
|
||||
|
||||
updatedNotes.push({
|
||||
...note._doc,
|
||||
...note,
|
||||
message: sortMsg,
|
||||
});
|
||||
}
|
||||
@@ -1445,7 +1448,7 @@ router.get(
|
||||
if ((theme && typeof theme === 'boolean') || theme === 'true') {
|
||||
const updatedLogs = [];
|
||||
for (const log of announcementLogs) {
|
||||
updatedLogs.push({ ...log._doc });
|
||||
updatedLogs.push({ ...log });
|
||||
}
|
||||
announcementLogs = formatNotes(updatedLogs, 20);
|
||||
announcementLogs = checkDuplicateDates(announcementLogs);
|
||||
@@ -1605,4 +1608,21 @@ function checkDuplicateDates(items) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function handleMonitorList(monitors) {
|
||||
if (monitors.length === 1) {
|
||||
return monitors[0].monitorId.name;
|
||||
}
|
||||
if (monitors.length === 2) {
|
||||
return `${monitors[0].monitorId.name} and ${monitors[1].monitorId.name}`;
|
||||
}
|
||||
if (monitors.length === 3) {
|
||||
return `${monitors[0].monitorId.name}, ${monitors[1].monitorId.name} and ${monitors[2].monitorId.name}`;
|
||||
}
|
||||
if (monitors.length > 3) {
|
||||
return `${monitors[0].monitorId.name}, ${
|
||||
monitors[1].monitorId.name
|
||||
} and ${monitors.length - 2} others`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -103,18 +103,23 @@ router.get('/:projectId/incident/:incidentId', async (req, res) => {
|
||||
projectId,
|
||||
idNumber,
|
||||
});
|
||||
incidentId = incidentId._id;
|
||||
const skip = req.query.skip || 0;
|
||||
const limit = req.query.limit || 10;
|
||||
const subscriberAlerts = await SubscriberAlertService.findBy(
|
||||
{ incidentId: incidentId, projectId: projectId },
|
||||
skip,
|
||||
limit
|
||||
);
|
||||
const count = await SubscriberAlertService.countBy({
|
||||
incidentId: incidentId,
|
||||
projectId: projectId,
|
||||
});
|
||||
|
||||
let subscriberAlerts = [],
|
||||
count = 0;
|
||||
if (incidentId) {
|
||||
incidentId = incidentId._id;
|
||||
subscriberAlerts = await SubscriberAlertService.findBy(
|
||||
{ incidentId: incidentId, projectId: projectId },
|
||||
skip,
|
||||
limit
|
||||
);
|
||||
count = await SubscriberAlertService.countBy({
|
||||
incidentId: incidentId,
|
||||
projectId: projectId,
|
||||
});
|
||||
}
|
||||
return sendListResponse(req, res, subscriberAlerts, count);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
|
||||
@@ -309,7 +309,7 @@ router.get('/sso/login', async function(req, res) {
|
||||
message: 'Domain not found.',
|
||||
});
|
||||
}
|
||||
const { 'saml-enabled': samlEnabled, samlSsoUrl } = sso;
|
||||
const { 'saml-enabled': samlEnabled, remoteLoginUrl } = sso;
|
||||
|
||||
if (!samlEnabled) {
|
||||
return sendErrorResponse(req, res, {
|
||||
@@ -321,9 +321,8 @@ router.get('/sso/login', async function(req, res) {
|
||||
const sp = new saml2.ServiceProvider({
|
||||
entity_id: sso.entityId,
|
||||
});
|
||||
|
||||
const idp = new saml2.IdentityProvider({
|
||||
sso_login_url: samlSsoUrl,
|
||||
sso_login_url: remoteLoginUrl,
|
||||
});
|
||||
|
||||
sp.create_login_request_url(idp, {}, function(error, login_url) {
|
||||
@@ -1316,6 +1315,14 @@ router.get('/users/:userId', getUser, isUserMasterAdmin, async function(
|
||||
router.delete('/:userId', getUser, isUserMasterAdmin, async function(req, res) {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
const authUserId = req.user.id;
|
||||
if (userId === authUserId) {
|
||||
const err = new Error(
|
||||
"Invalid operation! You can't perform this operation on your own account"
|
||||
);
|
||||
err.code = 400;
|
||||
throw err;
|
||||
}
|
||||
const masterUserId = req.user.id || null;
|
||||
const user = await UserService.deleteBy({ _id: userId }, masterUserId);
|
||||
return sendItemResponse(req, res, user);
|
||||
@@ -1330,6 +1337,14 @@ router.put('/:userId/restoreUser', getUser, isUserMasterAdmin, async function(
|
||||
) {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
const authUserId = req.user.id;
|
||||
if (userId === authUserId) {
|
||||
const err = new Error(
|
||||
"Invalid operation! You can't perform this operation on your own account"
|
||||
);
|
||||
err.code = 400;
|
||||
throw err;
|
||||
}
|
||||
const user = await UserService.restoreBy({
|
||||
_id: userId,
|
||||
deleted: true,
|
||||
@@ -1346,6 +1361,14 @@ router.put('/:userId/blockUser', getUser, isUserMasterAdmin, async function(
|
||||
) {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
const authUserId = req.user.id;
|
||||
if (userId === authUserId) {
|
||||
const err = new Error(
|
||||
"Invalid operation! You can't perform this operation on your own account"
|
||||
);
|
||||
err.code = 400;
|
||||
throw err;
|
||||
}
|
||||
const user = await UserService.updateOneBy(
|
||||
{ _id: userId },
|
||||
{ isBlocked: true }
|
||||
@@ -1362,6 +1385,14 @@ router.put('/:userId/unblockUser', getUser, isUserMasterAdmin, async function(
|
||||
) {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
const authUserId = req.user.id;
|
||||
if (userId === authUserId) {
|
||||
const err = new Error(
|
||||
"Invalid operation! You can't perform this operation on your own account"
|
||||
);
|
||||
err.code = 400;
|
||||
throw err;
|
||||
}
|
||||
const user = await UserService.updateOneBy(
|
||||
{ _id: userId },
|
||||
{ isBlocked: false }
|
||||
@@ -1372,6 +1403,66 @@ router.put('/:userId/unblockUser', getUser, isUserMasterAdmin, async function(
|
||||
}
|
||||
});
|
||||
|
||||
// Route
|
||||
// Description: Switch user account to admin mode.
|
||||
// Params:
|
||||
// Param 1: req.body-> {temporaryPassword}; req.headers-> {authorization}; req.user-> {id};
|
||||
// Returns: 200: Success, 400: Error; 401: Unauthorized; 500: Server Error.
|
||||
router.post(
|
||||
'/:userId/switchToAdminMode',
|
||||
getUser,
|
||||
isUserMasterAdmin,
|
||||
async function(req, res) {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
const authUserId = req.user.id;
|
||||
if (userId === authUserId) {
|
||||
const err = new Error(
|
||||
"Invalid operation! You can't perform this operation on your own account"
|
||||
);
|
||||
err.code = 400;
|
||||
throw err;
|
||||
}
|
||||
const { temporaryPassword } = req.body;
|
||||
const user = await UserService.switchToAdminMode(
|
||||
userId,
|
||||
temporaryPassword
|
||||
);
|
||||
return sendItemResponse(req, res, user);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Route
|
||||
// Description: Switch off admin mode for user account
|
||||
// Params:
|
||||
// Param 1: req.headers-> {authorization}; req.user-> {id};
|
||||
// Returns: 200: Success, 400: Error; 401: Unauthorized; 500: Server Error.
|
||||
router.post(
|
||||
'/:userId/exitAdminMode',
|
||||
getUser,
|
||||
isUserMasterAdmin,
|
||||
async function(req, res) {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
const authUserId = req.user.id;
|
||||
if (userId === authUserId) {
|
||||
const err = new Error(
|
||||
"Invalid operation! You can't perform this operation on your own account"
|
||||
);
|
||||
err.code = 400;
|
||||
throw err;
|
||||
}
|
||||
const user = await UserService.exitAdminMode(userId);
|
||||
return sendItemResponse(req, res, user);
|
||||
} catch (error) {
|
||||
return sendErrorResponse(req, res, error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
router.post('/:userId/addNote', getUser, isUserMasterAdmin, async function(
|
||||
req,
|
||||
res
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
*/
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
const mongoUrl = process.env['MONGO_URL'];
|
||||
const mongoUrl =
|
||||
process.env['MONGO_URL'] || 'mongodb://localhost:27017/fyipedb';
|
||||
|
||||
mongoose.set('useNewUrlParser', true);
|
||||
mongoose.set('useUnifiedTopology', true);
|
||||
|
||||
@@ -566,7 +566,7 @@ width="500" style="min-width: 500px;margin: 40px 50px;">
|
||||
'{{length}} : Length of the incident',
|
||||
'{{unsubscribeUrl}} : URL to unsubscribe from the monitor',
|
||||
],
|
||||
emailType: 'Subscriber Incident Acknowldeged',
|
||||
emailType: 'Subscriber Incident Acknowledged',
|
||||
subject: '{{projectName}} - {{monitorName}}: Incident Acknowledged',
|
||||
body: `
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
@@ -4163,6 +4163,516 @@ Fyipe Team.
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="st-Footer st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--divider" colspan="3" height="20" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="st-Divider">
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td bgcolor="#fdfdfd" colspan="2" height="1" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--divider" colspan="3" height="31" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Font st-Font--caption" style="border: 0; margin: 0;padding: 0; color: #8898aa; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 12px; line-height: 16px;">
|
||||
<span class="st-Delink st-Delink--footer" style="border: 0; margin: 0; padding: 0; color: #8898aa; text-decoration: none;">
|
||||
© {{year}} HackerBay Inc. | <span><a href={{unsubscribeUrl}}>Unsubscribe</a></span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--emailEnd" height="64" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<img border="0" style="height:1px; width:1px; border: 0; margin: 0; padding: 0; color: #000000; display: block; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 12px; font-weight: normal;" src="{{trackEmailAsViewedUrl}}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--emailEnd" colspan="3" height="64" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- /Wrapper -->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--emailEnd" height="64" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- /Background -->
|
||||
</body></html>
|
||||
`,
|
||||
},
|
||||
{
|
||||
allowedVariables: [
|
||||
'{{announcementTitle}} : Name of the maintenance event.',
|
||||
'{{announcementDescription}} : Description of the scheduled event.',
|
||||
'{{unsubscribeUrl}} : URL to unsubscribe from the monitor',
|
||||
'{{projectName}} : Name of project on which announcement is created',
|
||||
'{{resourcesAffected}} : List of monitors affected by scheduled maintenance event',
|
||||
],
|
||||
emailType: 'Subscriber Announcement Notification Created',
|
||||
subject: `Announcement Notification for {{projectName}}`,
|
||||
body: `
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=3Dutf-8">
|
||||
<meta name="viewport" content="width=3Ddevice-width">
|
||||
<title>Welcome to Fyipe.</title>
|
||||
<style>
|
||||
/**
|
||||
* IMPORTANT:
|
||||
* Please read before changing anything, CSS involved in our HTML emails is
|
||||
* extremely specific and written a certain way for a reason. It might not make
|
||||
* sense in a normal setting but Outlook loves it this way.
|
||||
*
|
||||
* !!! [override] prevents Yahoo Mail breaking media queries. It must be used
|
||||
* !!! at the beginning of every line of CSS inside a media query.
|
||||
* !!! Do not remove.
|
||||
*
|
||||
* !!! div[style*="margin: 16px 0"] allows us to target a weird margin
|
||||
* !!! bug in Android's email client.
|
||||
* !!! Do not remove.
|
||||
*
|
||||
* Also, the img files are hosted on S3. Please don't break these URLs!
|
||||
* The images are also versioned by date, so please update the URLs accordingly
|
||||
* if you create new versions
|
||||
*
|
||||
***/
|
||||
|
||||
|
||||
/**
|
||||
* # Root
|
||||
* - CSS resets and general styles go here.
|
||||
**/
|
||||
|
||||
html, body,
|
||||
a, span, div[style*="margin: 16px 0"] {
|
||||
border: 0 !important;
|
||||
margin: 0 !important;
|
||||
outline: 0 !important;
|
||||
padding: 0 !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
a, span,
|
||||
td, th {
|
||||
-webkit-font-smoothing: antialiased !important;
|
||||
-moz-osx-font-smoothing: grayscale !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* # Delink
|
||||
* - Classes for overriding clients which creates links out of things like
|
||||
* emails, addresses, phone numbers, etc.
|
||||
**/
|
||||
|
||||
span.st-Delink a {
|
||||
color: #000000 !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
/** Modifier: preheader */
|
||||
span.st-Delink.st-Delink--preheader a {
|
||||
color: white !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
/** */
|
||||
|
||||
/** Modifier: title */
|
||||
span.st-Delink.st-Delink--title a {
|
||||
color: #000000 !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
/** */
|
||||
|
||||
/** Modifier: footer */
|
||||
span.st-Delink.st-Delink--footer a {
|
||||
color: #8898aa !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
/** */
|
||||
|
||||
.ii a[href] {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* # Mobile
|
||||
* - This affects emails views in clients less than 600px wide.
|
||||
**/
|
||||
|
||||
@media all and (max-width: 600px) {
|
||||
|
||||
/**
|
||||
* # Wrapper
|
||||
**/
|
||||
|
||||
body[override] table.st-Wrapper,
|
||||
body[override] table.st-Width.st-Width--mobile {
|
||||
min-width: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* # Spacer
|
||||
**/
|
||||
|
||||
/** Modifier: gutter */
|
||||
body[override] td.st-Spacer.st-Spacer--gutter {
|
||||
width: 32px !important;
|
||||
}
|
||||
/** */
|
||||
|
||||
/** Modifier: kill */
|
||||
body[override] td.st-Spacer.st-Spacer--kill {
|
||||
width: 0 !important;
|
||||
}
|
||||
/** */
|
||||
|
||||
/** Modifier: emailEnd */
|
||||
body[override] td.st-Spacer.st-Spacer--emailEnd {
|
||||
height: 32px !important;
|
||||
}
|
||||
/** */
|
||||
|
||||
/**
|
||||
* # Font
|
||||
**/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Modifier: simplified */
|
||||
body[override] table.st-Header.st-Header--simplified td.st-Header-logo {
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
body[override] table.st-Header.st-Header--simplified td.st-Header-spacing{
|
||||
width: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* # Blocks
|
||||
**/
|
||||
|
||||
body[override] table.st-Blocks table.st-Blocks-inner {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
body[override] table.st-Blocks table.st-Blocks-inner table.st-Blocks-item td.st-Blocks-item-cell {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* # Button
|
||||
**/
|
||||
|
||||
body[override] table.st-Button {
|
||||
margin: 0 auto !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
body[override] table.st-Button td.st-Button-area,
|
||||
body[override] table.st-Button td.st-Button-area a.st-Button-link,
|
||||
body[override] table.st-Button td.st-Button-area span.st-Button-internal{
|
||||
height: 44px !important;
|
||||
line-height: 44px !important;
|
||||
font-size: 18px !important;
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body class="st-Email" bgcolor="f7f7f7" style="border: 0; margin: 0; padding: 0; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; min-width: 100%; width: 100%;" override="fix">
|
||||
|
||||
<!-- Background -->
|
||||
<table class="st-Background" bgcolor="f7f7f7" border="0" cellpadding="0" cellspacing="0" width="100%" style="border: 0; margin: 0; padding: 0;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border: 0; margin: 0; padding: 0;">
|
||||
|
||||
<!-- Wrapper -->
|
||||
<table class="st-Wrapper" align="center" bgcolor="ffffff" border="0" cellpadding="0" cellspacing="0" width="600" style="border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; margin: 0 auto; min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border: 0; margin: 0; padding: 0;">
|
||||
|
||||
|
||||
|
||||
<table class="st-Header st-Header--simplified st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--divider" colspan="4" height="19" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td align="left" style="height:80px; border: 0; margin: 0; padding: 0;">
|
||||
<div>
|
||||
<a style="border: 0; margin: 0; padding: 0; text-decoration: none;" href="https://fyipe.com">
|
||||
<img alt="Fyipe" border="0" style="height:50px; width:50px; border: 0; margin: 0; padding: 0; color: #000000; display: block; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 12px; font-weight: normal;" src="https://www.dropbox.com/s/dwawm02f1toxnm8/Fyipe-Icon.png?dl=0&raw=1">
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="st-Header-spacing" width="423" style="border: 0; margin: 0; padding: 0;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--divider" colspan="4" height="19" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="st-Divider">
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td bgcolor="#fdfdfd" colspan="2" height="1" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Font st-Font--body" style="color: #000000 !important; border:0;margin:0;padding:0; font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Ubuntu,sans-serif;font-size:16px;line-height:24px">
|
||||
<h3>Announcement notification for {{projectName}}</h3>
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Font st-Font--body" style="border: 0; margin: 0; padding: 0; color: #000000 !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 16px; line-height: 24px;">
|
||||
Hi.
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Font st-Font--body" style="border: 0; margin: 0; padding: 0; color: #000000 !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 16px; line-height: 24px;">
|
||||
The following announcement has just been made:
|
||||
</td>
|
||||
</tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
|
||||
width="500" style="min-width: 500px;margin: 40px 50px;">
|
||||
<tbody>
|
||||
<tr style="border-collapse:collapse">
|
||||
<td width="720" align="center" valign="top" style="padding:0;Margin:0"></td>
|
||||
</tr>
|
||||
<tr style="border-collapse:collapse">
|
||||
<td align="left"
|
||||
style="padding:0;Margin:0;padding-top:0px;padding-left:40px;padding-right:40px;padding-bottom:30px;background-color:#f7f8fa;border-radius: 5px">
|
||||
<table cellpadding="0" cellspacing="0" width="100%"
|
||||
style="border-collapse:collapse;border-spacing:0px">
|
||||
<tbody>
|
||||
<tr style="border-collapse:collapse">
|
||||
<td width="720" align="center" valign="top" style="padding:0;Margin:0">
|
||||
<table cellpadding="0" cellspacing="0" width="100%" role="presentation"
|
||||
style="border-collapse:collapse;border-spacing:0px">
|
||||
<tbody>
|
||||
<tr style="border-collapse:collapse">
|
||||
<td align="left" style="padding:0;Margin:0;padding-top:30px">
|
||||
<p
|
||||
style="Margin:0;font-size:16px;font-family:'inter','helvetica neue',helvetica,arial,sans-serif;line-height:30px;color:#424761">
|
||||
<strong>Title: </strong>
|
||||
<span>{{announcementTitle}}</span><br></p>
|
||||
<p
|
||||
style="Margin:0;font-size:16px;font-family:'inter','helvetica neue',helvetica,arial,sans-serif;line-height:30px;color:#424761">
|
||||
<strong>Content: </strong>
|
||||
<span>"{{announcementDescription}}"</span><br></p>
|
||||
|
||||
{{#if resourcesAffected}}
|
||||
<p style="Margin:0;font-size:16px;font-family:'inter','helvetica neue',helvetica,arial,sans-serif;line-height:30px;color:#424761">
|
||||
<strong>Resource Affected: </strong>
|
||||
<span>{{resourcesAffected}}</span><br></p>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Font st-Font--body" style="border: 0; margin: 0; padding: 0; color: #000000 !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 16px; line-height: 24px;">
|
||||
|
||||
{{#if statusPageUrl}}
|
||||
You can view the status of the incident here {{statusPageUrl}}
|
||||
{{/if}}
|
||||
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Font st-Font--body" style="border: 0; margin: 0; padding: 0; color: #000000 !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 16px; line-height: 24px;">
|
||||
|
||||
You are receiving this mail because you are subscribed to this monitor.
|
||||
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
<td class="st-Font st-Font--body" style="border: 0; margin: 0; padding: 0; color: #000000 !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 16px; line-height: 24px;">
|
||||
|
||||
Fyipe Team.
|
||||
|
||||
</td>
|
||||
<td class="st-Spacer st-Spacer--gutter" style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;" width="64">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12" style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
|
||||
<div class="st-Spacer st-Spacer--filler"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="st-Footer st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0" width="600" style="min-width: 600px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
|
||||
@@ -12,7 +12,7 @@ module.exports = {
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
'Subscriber Incident Acknowldeged': [
|
||||
'Subscriber Incident Acknowledged': [
|
||||
'{{userName}} : User display name.',
|
||||
'{{monitorName}} : Name of the monitor on which incident has occured.',
|
||||
'{{projectName}} : Name of the project on which the incident has occured.',
|
||||
|
||||
@@ -29,7 +29,7 @@ module.exports = {
|
||||
planId: 'plan_GoWIqpBpStiqQp',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
monitorLimit: 5,
|
||||
userLimit: 1,
|
||||
extraUserFee: 264,
|
||||
@@ -51,7 +51,7 @@ module.exports = {
|
||||
planId: 'plan_GoWKiTdQ6NiQFw',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
monitorLimit: 10,
|
||||
userLimit: 1,
|
||||
extraUserFee: 588,
|
||||
@@ -62,7 +62,7 @@ module.exports = {
|
||||
planId: 'plan_H9Iox3l2YqLTDR',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
monitorLimit: 9999,
|
||||
userLimit: 1,
|
||||
extraUserFee: 99,
|
||||
@@ -73,7 +73,7 @@ module.exports = {
|
||||
planId: 'plan_H9IlBKhsFz4hV2',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
monitorLimit: 10,
|
||||
userLimit: 1,
|
||||
extraUserFee: 588,
|
||||
@@ -98,7 +98,7 @@ module.exports = {
|
||||
planId: 'plan_GoVgJu5PKMLRJU',
|
||||
type: 'annual',
|
||||
amount: 264,
|
||||
details: '$264 / Year / User',
|
||||
details: '$22/mo per user paid annually. ',
|
||||
monitorLimit: 5,
|
||||
userLimit: 1,
|
||||
extraUserFee: 264,
|
||||
@@ -120,7 +120,7 @@ module.exports = {
|
||||
planId: 'plan_GoViZshjqzZ0vv',
|
||||
type: 'annual',
|
||||
amount: 588,
|
||||
details: '$588 / Year / User',
|
||||
details: '$49/mo per user paid annually. ',
|
||||
monitorLimit: 10,
|
||||
userLimit: 1,
|
||||
extraUserFee: 588,
|
||||
@@ -131,7 +131,7 @@ module.exports = {
|
||||
planId: 'plan_H9Ii6Qj3HLdtty',
|
||||
type: 'month',
|
||||
amount: 99,
|
||||
details: '$99 / Month / User',
|
||||
details: '$120 / Month / User',
|
||||
monitorLimit: 9999,
|
||||
userLimit: 1,
|
||||
extraUserFee: 99,
|
||||
@@ -142,7 +142,7 @@ module.exports = {
|
||||
planId: 'plan_H9IjvX2Flsvlcg',
|
||||
type: 'annual',
|
||||
amount: 1188,
|
||||
details: '$1188/ Year / User',
|
||||
details: '$99/mo per user paid annually. ',
|
||||
monitorLimit: 10,
|
||||
userLimit: 1,
|
||||
extraUserFee: 588,
|
||||
|
||||
@@ -26,7 +26,7 @@ module.exports = [
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
'{{length}} : Length of the incident',
|
||||
],
|
||||
smsType: 'Subscriber Incident Acknowldeged',
|
||||
smsType: 'Subscriber Incident Acknowledged',
|
||||
body:
|
||||
'{{projectName}} - {{incidentType}} incident on {{monitorName}} is acknowledged at {{incidentTime}}. You are receiving this message because you subscribed to this monitor.',
|
||||
},
|
||||
@@ -105,4 +105,14 @@ module.exports = [
|
||||
body:
|
||||
'Scheduled maintenance event for {{projectName}} - {{eventName}}, has been cancelled.',
|
||||
},
|
||||
{
|
||||
allowedVariables: [
|
||||
'{{announcementTitle}} : Name of the maintenance event.',
|
||||
'{{announcementDescription}} : Description of the scheduled event.',
|
||||
'{{projectName}} : Name of the project on which the event is created.',
|
||||
],
|
||||
smsType: 'Subscriber Announcement Notification Created',
|
||||
body:
|
||||
'An announcement has been made on {{projectName}}. Title: {{announcementTitle}}, Content: "{{announcementDescription}}."',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -9,7 +9,7 @@ module.exports = {
|
||||
'{{incident.customFields.*}} : The value of any incident custom field',
|
||||
'{{monitor.customFields.*}} : The value of any monitor custom field',
|
||||
],
|
||||
'Subscriber Incident Acknowldeged': [
|
||||
'Subscriber Incident Acknowledged': [
|
||||
'{{incidentTime}} : Time at which this incident occured.',
|
||||
'{{monitorName}} : Name of the monitor on which incident has occured.',
|
||||
'{{projectName}} : Name of the project on which the incident has occured.',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const INCIDENT_MAIL_TEMPLATE_TYPES = {
|
||||
CREATED_MAIL_TEMPLATE: 'Subscriber Incident Created',
|
||||
ACKNOWLEDGED_MAIL_TEMPLATE: 'Subscriber Incident Acknowldeged',
|
||||
ACKNOWLEDGED_MAIL_TEMPLATE: 'Subscriber Incident Acknowledged',
|
||||
RESOLVED_MAIL_TEMPLATE: 'Subscriber Incident Resolved',
|
||||
};
|
||||
|
||||
|
||||
132
backend/backend/middlewares/applicationScannerAuthorization.js
Normal file
132
backend/backend/middlewares/applicationScannerAuthorization.js
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
*
|
||||
* Copyright HackerBay, Inc.
|
||||
*
|
||||
*/
|
||||
const ApplicationScannerService = require('../services/applicationScannerService');
|
||||
const sendErrorResponse = require('../middlewares/response').sendErrorResponse;
|
||||
const ErrorService = require('../services/errorService');
|
||||
const CLUSTER_KEY = process.env.CLUSTER_KEY;
|
||||
module.exports = {
|
||||
isAuthorizedApplicationScanner: async function (req, res, next) {
|
||||
try {
|
||||
let applicationScannerKey, applicationScannerName, clusterKey, applicationScannerVersion;
|
||||
|
||||
if (req.params.applicationscannerkey) {
|
||||
applicationScannerKey = req.params.applicationscannerkey;
|
||||
} else if (req.query.applicationScannerKey) {
|
||||
applicationScannerKey = req.query.applicationscannerkey;
|
||||
} else if (req.headers['applicationscannerkey']) {
|
||||
applicationScannerKey = req.headers['applicationscannerkey'];
|
||||
} else if (req.headers['applicationscannerkey']) {
|
||||
applicationScannerKey = req.headers['applicationscannerkey'];
|
||||
} else if (req.body.applicationScannerKey) {
|
||||
applicationScannerKey = req.body.applicationScannerKey;
|
||||
} else {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'applicationScanner Key not found.',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.params.applicationscannername) {
|
||||
applicationScannerName = req.params.applicationscannername;
|
||||
} else if (req.query.applicationscannername) {
|
||||
applicationScannerName = req.query.applicationscannername;
|
||||
} else if (req.headers['applicationscannername']) {
|
||||
applicationScannerName = req.headers['applicationscannername'];
|
||||
} else if (req.headers['applicationscannername']) {
|
||||
applicationScannerName = req.headers['applicationscannername'];
|
||||
} else if (req.body.applicationscannerName) {
|
||||
applicationScannerName = req.body.applicationscannername;
|
||||
} else {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'applicationScanner Name not found.',
|
||||
});
|
||||
}
|
||||
|
||||
if (req.params.clusterKey) {
|
||||
clusterKey = req.params.clusterkey;
|
||||
} else if (req.query.clusterKey) {
|
||||
clusterKey = req.query.clusterkey;
|
||||
} else if (req.headers['clusterKey']) {
|
||||
clusterKey = req.headers['clusterKey'];
|
||||
} else if (req.headers['clusterkey']) {
|
||||
clusterKey = req.headers['clusterkey'];
|
||||
} else if (req.body.clusterKey) {
|
||||
clusterKey = req.body.clusterKey;
|
||||
}
|
||||
|
||||
if (req.params.applicationscannerversion) {
|
||||
applicationScannerVersion = req.params.applicationscannerversion;
|
||||
} else if (req.query.applicationscannerversion) {
|
||||
applicationScannerVersion = req.query.applicationscannerversion;
|
||||
} else if (req.headers['applicationscannerversion']) {
|
||||
applicationScannerVersion = req.headers['applicationscannerversion'];
|
||||
} else if (req.body.applicationscannerversion) {
|
||||
applicationScannerVersion = req.body.applicationscannerversion;
|
||||
}
|
||||
|
||||
let applicationScanner = null;
|
||||
|
||||
if (clusterKey && clusterKey === CLUSTER_KEY) {
|
||||
// if cluster key matches then just query by applicationScanner name,
|
||||
// because if the applicationScanner key does not match, we can update applicationScanner key later
|
||||
// without updating mongodb database manually.
|
||||
applicationScanner = await ApplicationScannerService.findOneBy({ applicationScannerName });
|
||||
} else {
|
||||
applicationScanner = await ApplicationScannerService.findOneBy({ applicationScannerKey, applicationScannerName });
|
||||
}
|
||||
|
||||
if (!applicationScanner && (!clusterKey || clusterKey !== CLUSTER_KEY)) {
|
||||
return sendErrorResponse(req, res, {
|
||||
code: 400,
|
||||
message: 'applicationScanner key and applicationScanner name do not match.',
|
||||
});
|
||||
}
|
||||
|
||||
if (!applicationScanner) {
|
||||
//create a new applicationScanner.
|
||||
applicationScanner = await ApplicationScannerService.create({
|
||||
applicationScannerKey,
|
||||
applicationScannerName,
|
||||
applicationScannerVersion,
|
||||
});
|
||||
}
|
||||
|
||||
if (applicationScanner.applicationScannerKey !== applicationScannerKey) {
|
||||
//update applicationScanner key becasue it does not match.
|
||||
await ApplicationScannerService.updateOneBy(
|
||||
{
|
||||
applicationScannerName,
|
||||
},
|
||||
{ applicationScannerKey }
|
||||
);
|
||||
}
|
||||
req.applicationScanner = {};
|
||||
req.applicationScanner.id = applicationScanner._id;
|
||||
await ApplicationScannerService.updateApplicationScannerStatus(applicationScanner._id);
|
||||
|
||||
//Update applicationScanner version
|
||||
const applicationScannerValue = await ApplicationScannerService.findOneBy({
|
||||
applicationScannerKey,
|
||||
applicationScannerName,
|
||||
});
|
||||
|
||||
if (!applicationScannerValue.version || applicationScannerValue.version !== applicationScannerVersion) {
|
||||
await ApplicationScannerService.updateOneBy(
|
||||
{
|
||||
applicationScannerName,
|
||||
},
|
||||
{ version: applicationScannerVersion }
|
||||
);
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
ErrorService.log('applicationScannerAuthorization.isAuthorizedApplicationScanner', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const GridFsStorage = require('multer-gridfs-storage');
|
||||
const { GridFsStorage } = require('multer-gridfs-storage');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const mongoUri = process.env['MONGO_URL'];
|
||||
|
||||
21
backend/backend/models/applicationScanner.js
Normal file
21
backend/backend/models/applicationScanner.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
*
|
||||
* Copyright HackerBay, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
const mongoose = require('../config/db');
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
const applicationScannerSchema = new Schema({
|
||||
createdAt: { type: Date, default: Date.now },
|
||||
applicationScannerKey: { type: String },
|
||||
applicationScannerName: { type: String },
|
||||
version: { type: String },
|
||||
lastAlive: { type: Date, default: Date.now },
|
||||
deleted: { type: Boolean, default: false },
|
||||
deletedAt: { type: Date },
|
||||
applicationScannerImage: { type: String },
|
||||
});
|
||||
|
||||
module.exports = mongoose.model('applicationScanner', applicationScannerSchema);
|
||||
58
backend/backend/models/automatedScripts.js
Normal file
58
backend/backend/models/automatedScripts.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const mongoose = require('../config/db');
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
const automatedScriptSchema = new Schema(
|
||||
{
|
||||
name: String,
|
||||
script: String,
|
||||
scriptType: String,
|
||||
slug: String,
|
||||
projectId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Project',
|
||||
index: true,
|
||||
},
|
||||
deleted: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
},
|
||||
deletedById: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
index: true,
|
||||
},
|
||||
successEvent: [
|
||||
{
|
||||
automatedScript: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'AutomationSript',
|
||||
index: true,
|
||||
},
|
||||
callSchedule: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Schedule',
|
||||
index: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
failureEvent: [
|
||||
{
|
||||
automatedScript: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'AutomationSript',
|
||||
index: true,
|
||||
},
|
||||
callSchedule: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Schedule',
|
||||
index: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
module.exports = mongoose.model('AutomationSript', automatedScriptSchema);
|
||||
46
backend/backend/models/automationScriptsLog.js
Normal file
46
backend/backend/models/automationScriptsLog.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const mongoose = require('../config/db');
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
const automationSriptLogSchema = new Schema(
|
||||
{
|
||||
automationScriptId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'AutomationSript',
|
||||
index: true,
|
||||
},
|
||||
triggerByUser: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
},
|
||||
triggerByScript: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'AutomationSript',
|
||||
},
|
||||
triggerByIncident: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Incident',
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
enum: ['success', 'running', 'failed'],
|
||||
default: 'running',
|
||||
},
|
||||
deleted: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
deletedAt: {
|
||||
type: Date,
|
||||
},
|
||||
deletedById: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
index: true,
|
||||
},
|
||||
executionTime: Number,
|
||||
consoleLogs: [String],
|
||||
error: String,
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
module.exports = mongoose.model('AutomationSriptLog', automationSriptLogSchema);
|
||||
37
backend/backend/models/defaultManager.js
Normal file
37
backend/backend/models/defaultManager.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
*
|
||||
* Copyright HackerBay, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
const mongoose = require('../config/db');
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
const defaultManagerSchema = new Schema(
|
||||
{
|
||||
store: {
|
||||
type: Object,
|
||||
default: {
|
||||
module: 'fyipe-le-store',
|
||||
},
|
||||
},
|
||||
challenges: {
|
||||
'http-01': {
|
||||
type: Object,
|
||||
default: {
|
||||
module: 'fyipe-acme-http-01',
|
||||
},
|
||||
},
|
||||
},
|
||||
renewOffset: { type: String, default: '-45d' },
|
||||
renewStagger: { type: String, default: '3d' },
|
||||
accountKeyType: String,
|
||||
serverKeyType: String,
|
||||
subscriberEmail: String,
|
||||
agreeToTerms: { type: Boolean, default: true },
|
||||
deleted: { type: Boolean, default: false },
|
||||
deletedAt: Date,
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
module.exports = mongoose.model('DefaultManager', defaultManagerSchema);
|
||||
@@ -15,13 +15,14 @@ const emailTemplateSchema = new Schema({
|
||||
type: String,
|
||||
enum: [
|
||||
'Subscriber Incident Created',
|
||||
'Subscriber Incident Acknowldeged',
|
||||
'Subscriber Incident Acknowledged',
|
||||
'Subscriber Incident Resolved',
|
||||
'Investigation note is created',
|
||||
'Subscriber Scheduled Maintenance Created',
|
||||
'Subscriber Scheduled Maintenance Note',
|
||||
'Subscriber Scheduled Maintenance Resolved',
|
||||
'Subscriber Scheduled Maintenance Cancelled',
|
||||
'Subscriber Announcement Notification Created',
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
|
||||
@@ -22,8 +22,24 @@ const monitorSchema = new Schema({
|
||||
type: Schema.Types.String,
|
||||
},
|
||||
response: Object,
|
||||
monitorId: { type: String, ref: 'Monitor', index: true }, // which monitor does this incident belongs to.
|
||||
notificationId: { type: String, ref: 'Notification', index: true },
|
||||
monitors: [
|
||||
{
|
||||
monitorId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Monitor',
|
||||
index: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
notifications: [
|
||||
{
|
||||
notificationId: {
|
||||
type: String,
|
||||
ref: 'Notification',
|
||||
index: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
incidentPriority: {
|
||||
type: String,
|
||||
ref: 'IncidentPriority',
|
||||
@@ -96,6 +112,7 @@ const monitorSchema = new Schema({
|
||||
},
|
||||
|
||||
deletedById: { type: String, ref: 'User', index: true },
|
||||
// has this incident breached communication sla
|
||||
breachedCommunicationSla: { type: Boolean, default: false },
|
||||
customFields: [
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user