Adds support for parentId for monitors

This commit is contained in:
ksjaay
2025-11-30 16:14:21 +00:00
committed by KSJaay
parent 61baa59640
commit 7ef7687668
20 changed files with 164 additions and 8 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
import { toast } from 'react-toastify';
import { observer } from 'mobx-react-lite';
// import local files
import Dropdown from '../../ui/dropdown';
@@ -88,4 +89,4 @@ const IncidentContentImpact = () => {
IncidentContentImpact.displayName = 'IncidentContentImpact';
export default IncidentContentImpact;
export default observer(IncidentContentImpact);
@@ -4,6 +4,7 @@ import { Input } from '@lunalytics/ui';
// import local files
import MonitorInitialDropdown from './type';
import MonitorIconSelect from './icons';
import MonitorParentSelect from './parent';
interface MonitorInitialTypeProps {
inputs: any;
@@ -51,6 +52,8 @@ const MonitorInitialType = ({
)}
<MonitorIconSelect inputs={inputs} handleInput={handleInput} />
<MonitorParentSelect inputs={inputs} handleInput={handleInput} />
</div>
);
};
@@ -0,0 +1,71 @@
import { observer } from 'mobx-react-lite';
import useDropdown from '../../../../../hooks/useDropdown';
import Dropdown from '../../../../ui/dropdown';
import useContextStore from '../../../../../context';
const MonitorParentSelect = ({
inputs,
handleInput,
}: {
inputs: any;
handleInput: (key: string, value: any) => void;
}) => {
const {
globalStore: { allMonitors },
} = useContextStore();
const { toggleDropdown, dropdownIsOpen } = useDropdown(true);
const onSelect = (monitorId: string | null) => {
handleInput('parentId', monitorId);
toggleDropdown();
};
const monitors = allMonitors.filter(
(monitor) => monitor.monitorId !== inputs.monitorId
);
return (
<div className="luna-input-wrapper">
<label className="input-label">Monitor Parent</label>
<label className="luna-input-subtitle">
If parent is down notification won&#39;t be sent for this monitor
</label>
<div>
<Dropdown.Container
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
position="center"
>
<Dropdown.Trigger
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
color="var(--lunaui-accent-900)"
asInput
>
{inputs.parentId
? monitors.find(
(monitor) => monitor.monitorId === inputs.parentId
)?.name
: 'No parent'}
</Dropdown.Trigger>
<Dropdown.List isOpen={dropdownIsOpen} fullWidth>
<Dropdown.Item onClick={() => onSelect(null)}>
No parent
</Dropdown.Item>
{monitors.map((monitor) => (
<Dropdown.Item
key={monitor.monitorId}
onClick={() => onSelect(monitor.monitorId)}
>
{monitor.name}
</Dropdown.Item>
))}
</Dropdown.List>
</Dropdown.Container>
</div>
</div>
);
};
export default observer(MonitorParentSelect);
+2
View File
@@ -33,6 +33,7 @@ const handleMonitor = async (
const {
name,
parentId,
type,
url,
method,
@@ -57,6 +58,7 @@ const handleMonitor = async (
const query = await createPostRequest(apiPath, {
name,
parentId,
type,
url,
method,
+29 -2
View File
@@ -1,12 +1,12 @@
{
"name": "lunalytics",
"version": "0.10.12",
"version": "0.10.13",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "lunalytics",
"version": "0.10.12",
"version": "0.10.13",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"@dnd-kit/core": "6.3.1",
@@ -50,6 +50,7 @@
"react-window": "1.8.11",
"recharts": "3.2.1",
"swapy": "1.0.5",
"systeminformation": "^5.27.11",
"ua-parser-js": "2.0.3",
"uuid": "11.1.0",
"winston": "3.17.0",
@@ -12851,6 +12852,32 @@
"integrity": "sha512-XEzy5HCw7yESb7QajKTBJ+NA/BL0kAKlE7XhSMPZawF740X4ws/5CFtXRsVDQ+EztISC759a9+DJM9mxjz4hXg==",
"license": "GPL-3.0"
},
"node_modules/systeminformation": {
"version": "5.27.11",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.27.11.tgz",
"integrity": "sha512-K3Lto/2m3K2twmKHdgx5B+0in9qhXK4YnoT9rIlgwN/4v7OV5c8IjbeAUkuky/6VzCQC7iKCAqi8rZathCdjHg==",
"license": "MIT",
"os": [
"darwin",
"linux",
"win32",
"freebsd",
"openbsd",
"netbsd",
"sunos",
"android"
],
"bin": {
"systeminformation": "lib/cli.js"
},
"engines": {
"node": ">=8.0.0"
},
"funding": {
"type": "Buy me a coffee",
"url": "https://www.buymeacoffee.com/systeminfo"
}
},
"node_modules/tar-fs": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "lunalytics",
"version": "0.10.12",
"version": "0.10.13",
"description": "Open source Node.js server/website monitoring tool",
"private": true,
"author": "KSJaay <ksjaay@gmail.com>",
@@ -81,6 +81,7 @@
"react-window": "1.8.11",
"recharts": "3.2.1",
"swapy": "1.0.5",
"systeminformation": "^5.27.11",
"ua-parser-js": "2.0.3",
"uuid": "11.1.0",
"winston": "3.17.0",
+22
View File
@@ -0,0 +1,22 @@
// import local files
import SQLite from '../../server/database/sqlite/setup.js';
import logger from '../../server/utils/logger.js';
const infomation = {
title: 'Add parentId to monitor table',
description: 'Adds parentId to monitor table',
version: '0.10.13',
};
const migrate = async () => {
const client = await SQLite.connect();
await client.schema.alterTable('monitor', (table) => {
table.string('parentId').nullable().defaultTo(null);
});
logger.info('Migrations', { message: '0.10.13 has been applied' });
return;
};
export { infomation, migrate };
+2
View File
@@ -11,6 +11,7 @@ import { migrate as migrateIncidentEmail } from './0-9-4.js';
import { migrate as migrateMonitor } from './0-9-5.js';
import { migrate as migrateMonitorJson } from './0-9-7.js';
import { migrate as migrateUiOverhaul } from './0-10-0.js';
import { migrate as migrateMonitorParent } from './0-10-13.js';
const migrationList = {
'0.4.0': migrateTcpUpdate,
@@ -25,6 +26,7 @@ const migrationList = {
'0.9.5': migrateMonitor,
'0.9.7': migrateMonitorJson,
'0.10.0': migrateUiOverhaul,
'0.10.13': migrateMonitorParent,
};
export default migrationList;
+15
View File
@@ -197,6 +197,21 @@ class Master {
if (!hasOutage && !hasRecovered) return;
if (!monitor.notificationId) return;
if (hasOutage && monitor.parentId) {
const parentMonitor = await fetchMonitor(monitor.parentId).catch(
() => false
);
if (parentMonitor) {
const isDown = await isMonitorDown(
parentMonitor.monitorId,
parentMonitor.retry
);
if (isDown) return;
}
}
const notification = await fetchNotificationById(monitor.notificationId);
if (
+1
View File
@@ -2,6 +2,7 @@ import { parseJsonOrArray } from '../../utils/parser.js';
const clean = ({ heartbeats = [], ...monitor }, includeHeartbeats = true) => ({
monitorId: monitor.monitorId,
parentId: monitor.parentId || null,
name: monitor.name,
url: monitor.url,
retry: parseInt(monitor.retry),
+1
View File
@@ -7,6 +7,7 @@ const clean = (
includeCert = true
) => ({
monitorId: monitor.monitorId,
parentId: monitor.parentId || null,
name: monitor.name,
url: monitor.url,
retry: parseInt(monitor.retry),
+1
View File
@@ -7,6 +7,7 @@ export const clean = (
includeCert = true
) => ({
monitorId: monitor.monitorId,
parentId: monitor.parentId || null,
name: monitor.name,
url: monitor.url,
retry: parseInt(monitor.retry),
+1
View File
@@ -5,6 +5,7 @@ export const clean = (
includeHeartbeats = true
) => ({
monitorId: monitor.monitorId,
parentId: monitor.parentId || null,
name: monitor.name,
url: monitor.url,
retry: parseInt(monitor.retry),
+1
View File
@@ -2,6 +2,7 @@ import { parseJsonOrArray } from '../../utils/parser.js';
const clean = ({ heartbeats = [], ...monitor }, includeHeartbeats = true) => ({
monitorId: monitor.monitorId,
parentId: monitor.parentId || null,
name: monitor.name,
url: monitor.url,
retry: parseInt(monitor.retry),
+1
View File
@@ -5,6 +5,7 @@ export const monitorTable = async (client) => {
await client.schema.createTable('monitor', (table) => {
table.increments('id');
table.string('monitorId').notNullable().primary();
table.string('parentId').defaultTo(null);
table.string('name').notNullable();
table.string('url').notNullable();
table.integer('port').defaultTo(null);
+1 -4
View File
@@ -17,10 +17,7 @@ import initialiseCronJobs from './utils/cron.js';
import migrateDatabase from '../scripts/migrate.js';
import addInviteToCookie from './middleware/addInviteToCookie.js';
import { loadIcons } from './utils/icons.js';
import {
getVersionInfo,
startVersionCheck
} from './utils/checkVersion.js';
import { getVersionInfo, startVersionCheck } from './utils/checkVersion.js';
const app = express();
+2
View File
@@ -25,6 +25,7 @@ export const defaultMonitorData = (body) => ({
url: body.url,
interval: body.interval ?? 60,
monitorId: body.monitorId,
parentId: body.parentId ?? null,
retry: body.retry ?? 1,
retryInterval: body.retryInterval ?? 60,
requestTimeout: body.requestTimeout ?? 60,
@@ -51,6 +52,7 @@ export const formatMonitorData = (body, email) => {
url: body.url,
interval: body.interval,
monitorId: body.monitorId,
parentId: body.parentId,
retry: body.retry,
retryInterval: body.retryInterval,
requestTimeout: body.requestTimeout,
+3
View File
@@ -4,6 +4,7 @@ import { cleanMonitor } from '../../../server/class/monitor/index';
describe('Monitor - Class', () => {
const monitor = {
monitorId: '4d048471-9e85-428b-8050-4238f6033478',
parentId: '4d048471-9e85-428b-8050-4238f6033479',
name: 'Lunalytics',
url: 'https://demo.lunalytics.xyz/api/status',
createdAt: '2025-08-23T17:00:00.000Z',
@@ -48,6 +49,7 @@ describe('Monitor - Class', () => {
it('should return valid monitor using cleanPartialMonitor', () => {
expect(cleanMonitor(monitor, false, false)).toEqual({
monitorId: '4d048471-9e85-428b-8050-4238f6033478',
parentId: '4d048471-9e85-428b-8050-4238f6033479',
name: 'Lunalytics',
url: 'https://demo.lunalytics.xyz/api/status',
createdAt: '2025-08-23T17:00:00.000Z',
@@ -87,6 +89,7 @@ describe('Monitor - Class', () => {
})
).toEqual({
monitorId: '4d048471-9e85-428b-8050-4238f6033478',
parentId: '4d048471-9e85-428b-8050-4238f6033479',
name: 'Lunalytics',
url: 'https://demo.lunalytics.xyz/api/status',
createdAt: '2025-08-23T17:00:00.000Z',
@@ -154,6 +154,7 @@ describe('Add Monitor - Middleware', () => {
requestTimeout: fakeRequest.body.requestTimeout,
notificationId: fakeRequest.body.notificationId,
notificationType: fakeRequest.body.notificationType,
parentId: null,
headers: JSON.stringify(fakeRequest.body.headers),
body: JSON.stringify(fakeRequest.body.body),
email: user.email,
@@ -276,6 +277,7 @@ describe('Add Monitor - Middleware', () => {
requestTimeout: fakeRequest.body.requestTimeout,
notificationId: fakeRequest.body.notificationId,
notificationType: fakeRequest.body.notificationType,
parentId: null,
email: user.email,
port: fakeRequest.body.port,
valid_status_codes: '',
@@ -154,6 +154,7 @@ describe('Edit Monitor - Middleware', () => {
requestTimeout: fakeRequest.body.requestTimeout,
notificationId: fakeRequest.body.notificationId,
notificationType: fakeRequest.body.notificationType,
parentId: null,
body: JSON.stringify(fakeRequest.body.body),
headers: JSON.stringify(fakeRequest.body.headers),
email: user.email,
@@ -275,6 +276,7 @@ describe('Edit Monitor - Middleware', () => {
requestTimeout: fakeRequest.body.requestTimeout,
notificationId: fakeRequest.body.notificationId,
notificationType: fakeRequest.body.notificationType,
parentId: null,
email: user.email,
port: fakeRequest.body.port,
valid_status_codes: '',