Skip to content

Commit

Permalink
Adds support for cloning, and pausing monitors
Browse files Browse the repository at this point in the history
  • Loading branch information
KSJaay committed Dec 24, 2024
1 parent 4641fda commit fcc14a4
Show file tree
Hide file tree
Showing 17 changed files with 212 additions and 29 deletions.
5 changes: 4 additions & 1 deletion app/components/home/monitor/layout/card.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ const MonitorCard = ({ monitor = {} }) => {
<div>{uptimePercentage}%</div>
</div>
</div>
<div className="home-monitor-status">
<div
className="home-monitor-status"
style={{ filter: `grayscale(${monitor.paused ? 0.75 : 0})` }}
>
<h1>Status</h1>
<StatusBar heartbeats={heartbeats} />
</div>
Expand Down
6 changes: 6 additions & 0 deletions app/components/icons/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
FaPlus,
FaSignOutAlt,
FaUsers,
FaClone,
} from 'react-icons/fa';
import { FiLayout } from 'react-icons/fi';
import { HiStatusOffline, HiStatusOnline } from 'react-icons/hi';
Expand All @@ -23,6 +24,8 @@ import {
FaBars,
FaTrashCan,
FaFilter,
FaPause,
FaPlay,
} from 'react-icons/fa6';
import { IoArrowBack, IoColorPalette, IoGrid, IoReload } from 'react-icons/io5';
import { RiStackFill } from 'react-icons/ri';
Expand All @@ -37,10 +40,13 @@ export {
FaChevronRight,
FaChevronUp,
FaCircleCheck,
FaClone,
FaCog,
FaEllipsisVertical,
FaFilter,
FaHome,
FaPause,
FaPlay,
FaPlus,
FaSignOutAlt,
FaTrashCan,
Expand Down
118 changes: 100 additions & 18 deletions app/components/monitor/menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@ import { useNavigate } from 'react-router-dom';
import Button from '../ui/button';
import useContextStore from '../../context';
import MonitorModal from '../modal/monitor/delete';
import { createGetRequest } from '../../services/axios';
import { createGetRequest, createPostRequest } from '../../services/axios';
import MonitorConfigureModal from '../modal/monitor/configure';
import { FaTrashCan, MdEdit } from '../icons';
import { FaClone, FaEllipsisVertical, FaTrashCan, MdEdit } from '../icons';
import { FaPlay, FaPause } from '../icons';
import Dropdown from '../ui/dropdown';
import useDropdown from '../../hooks/useDropdown';

const MonitorMenu = ({ name = 'Unknown', monitorId }) => {
const {
modalStore: { openModal, closeModal },
globalStore: { getMonitor, editMonitor, removeMonitor },
globalStore: { addMonitor, getMonitor, editMonitor, removeMonitor },
userStore: { user },
} = useContextStore();
const { toggleDropdown, dropdownIsOpen } = useDropdown();
const navigate = useNavigate();

const monitor = getMonitor(monitorId);
const isEditor = user.permission <= 3;

const handleConfirm = async () => {
Expand All @@ -37,9 +42,38 @@ const MonitorMenu = ({ name = 'Unknown', monitorId }) => {
navigate('/');
};

const handleEdit = () => {
const monitor = getMonitor(monitorId);
const handlePause = async () => {
try {
await createPostRequest('/api/monitor/pause', {
monitorId,
pause: !monitor.paused,
});

editMonitor({ ...monitor, paused: !monitor.paused });

toast.success(
monitor.paused
? 'Monitor resumed successfully!'
: 'Monitor paused successfully!'
);
} catch (error) {
console.log(error);
toast.error('Error occurred while pausing monitor!');
}
};

const handleClone = () => {
openModal(
<MonitorConfigureModal
monitor={monitor}
closeModal={closeModal}
handleMonitorSubmit={addMonitor}
/>,
false
);
};

const handleEdit = () => {
openModal(
<MonitorConfigureModal
monitor={monitor}
Expand All @@ -61,6 +95,37 @@ const MonitorMenu = ({ name = 'Unknown', monitorId }) => {
);
};

const options = [
{
value: 'Clone',
icon: <FaClone style={{ width: '20px', height: '20px' }} />,
onClick: handleClone,
id: 'monitor-pause-button',
},
{
value: 'Edit',
icon: <MdEdit style={{ width: '20px', height: '20px' }} />,
onClick: handleEdit,
id: 'monitor-edit-button',
},
{
value: 'Delete',
icon: <FaTrashCan style={{ width: '20px', height: '20px' }} />,
onClick: handleDelete,
id: 'monitor-delete-button',
},
{
value: monitor.paused ? 'Resume' : 'Pause',
icon: monitor.paused ? (
<FaPlay style={{ width: '20px', height: '20px' }} />
) : (
<FaPause style={{ width: '20px', height: '20px' }} />
),
onClick: handlePause,
id: 'monitor-clone-button',
},
];

return (
<div className="monitor-view-menu-container">
<div className="monitor-view-menu-name" id="monitor-view-menu-name">
Expand All @@ -70,20 +135,37 @@ const MonitorMenu = ({ name = 'Unknown', monitorId }) => {
{/* <Button iconLeft={<FaTrashCan style={{ width: '20px', height: '20px' }} />}>Duplicate</Button> */}
{isEditor && (
<>
<Button
id="monitor-edit-button"
iconLeft={<MdEdit style={{ width: '20px', height: '20px' }} />}
onClick={handleEdit}
>
Edit
</Button>
<Button
id="monitor-delete-button"
iconLeft={<FaTrashCan style={{ width: '20px', height: '20px' }} />}
onClick={handleDelete}
{options.map((option) => (
<Button
key={option.value}
id={option.id}
iconLeft={option.icon}
onClick={option.onClick}
>
{option.value}
</Button>
))}

<Dropdown.Container
toggleDropdown={toggleDropdown}
isOpen={dropdownIsOpen}
position="left"
>
Delete
</Button>
<Dropdown.Trigger
toggleDropdown={toggleDropdown}
isOpen={dropdownIsOpen}
>
<FaEllipsisVertical style={{ width: '25px', height: '25px' }} />
</Dropdown.Trigger>
<Dropdown.List isOpen={dropdownIsOpen}>
{options.map((option) => (
<Dropdown.Item key={option.value} onClick={option.onClick}>
{option.icon}
{option.value}
</Dropdown.Item>
))}
</Dropdown.List>
</Dropdown.Container>
</>
)}
</div>
Expand Down
21 changes: 21 additions & 0 deletions app/components/monitor/menu.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@use '../../styles/breakpoints.scss' as *;

.monitor-view-menu-container {
display: flex;
gap: 10px;
Expand All @@ -13,3 +15,22 @@
font-size: var(--font-2xl);
font-weight: bold;
}

.monitor-view-menu-container .dropdown {
display: none;
}

@include mobile {
.monitor-view-menu-container {
justify-content: center;
align-items: center;
}

.monitor-view-menu-container .button {
display: none;
}

.monitor-view-menu-container .dropdown {
display: block;
}
}
5 changes: 5 additions & 0 deletions app/pages/home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ const Home = () => {

return true;
})
.sort((a, b) => {
if (a.paused && !b.paused) return 1;
if (!a.paused && b.paused) return -1;
return a.name.localeCompare(b.name);
})
.map((monitor, index) => {
if (layout === 'list') {
return (
Expand Down
10 changes: 8 additions & 2 deletions docs/api/monitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ There are various restrictions applied to the monitor data. The following are so
| uptimePercentage | number | Uptime percentage for the monitor over the last 24 hours |
| averageHeartbeatLatency | number | Average latency for the monitor over the last 24 hours |
| showFilters | boolean | Used to check if hourly heartbeats are available |
| paused | boolean | Boolean if the monitor is paused |

### Example Partial Monitor

Expand All @@ -83,7 +84,8 @@ There are various restrictions applied to the monitor data. The following are so
"port": null,
"uptimePercentage": 83,
"averageHeartbeatLatency": 38,
"showFilters": true
"showFilters": true,
"paused": false
}
```

Expand All @@ -104,7 +106,8 @@ There are various restrictions applied to the monitor data. The following are so
"port": 2308,
"uptimePercentage": 83,
"averageHeartbeatLatency": 38,
"showFilters": false
"showFilters": false,
"paused": false
}
```

Expand All @@ -130,6 +133,7 @@ There are various restrictions applied to the monitor data. The following are so
| uptimePercentage | number | Uptime percentage for the monitor over the last 24 hours |
| averageHeartbeatLatency | number | Average latency for the monitor over the last 24 hours |
| showFilters | boolean | Used to check if hourly heartbeats are available |
| paused | boolean | Boolean if the monitor is paused |
| heartbeats | Array<[Heartbeat](#heartbeat-structure)> | Array of monitor heartbeats |
| cert | [Certificate](#certificate-structure) | Information about the certificate |

Expand All @@ -155,6 +159,7 @@ There are various restrictions applied to the monitor data. The following are so
"uptimePercentage": 83,
"averageHeartbeatLatency": 38,
"showFilters": true,
"paused": false,
"heartbeats": [
{
"id": 38,
Expand Down Expand Up @@ -204,6 +209,7 @@ There are various restrictions applied to the monitor data. The following are so
"uptimePercentage": 83,
"averageHeartbeatLatency": 38,
"showFilters": false,
"paused": false,
"heartbeats": [
{
"id": 38,
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lunalytics",
"version": "0.7.0",
"version": "0.7.2",
"description": "Open source Node.js server/website monitoring tool",
"private": true,
"author": "KSJaay <ksjaay@gmail.com>",
Expand Down
7 changes: 7 additions & 0 deletions server/cache/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class Master {
return;
}

if (monitor.paused) return;

if (this.timeouts.has(monitorId)) {
clearTimeout(this.timeouts.get(monitorId));
}
Expand Down Expand Up @@ -146,6 +148,11 @@ class Master {
});
}
}

removeMonitor(monitorId) {
clearTimeout(this.timeouts.get(monitorId));
this.timeouts.delete(monitorId);
}
}

const cache = new Master();
Expand Down
2 changes: 1 addition & 1 deletion server/class/certificate.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const parseJson = (str) => {
};

const cleanCertificate = (certificate) => ({
isValid: certificate.isValid,
isValid: certificate.isValid == '1',
issuer: parseJson(certificate.issuer),
validFrom: certificate.validFrom,
validTill: certificate.validTill,
Expand Down
6 changes: 4 additions & 2 deletions server/class/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export const cleanPartialMonitor = (monitor) => ({
notificationType: monitor.notificationType,
uptimePercentage: monitor.uptimePercentage,
averageHeartbeatLatency: monitor.averageHeartbeatLatency,
showFilters: monitor.showFilters || false,
showFilters: monitor.showFilters == '1',
paused: monitor.paused == '1',
});

export const cleanMonitor = ({ heartbeats = [], cert, ...monitor }) => ({
Expand All @@ -47,7 +48,8 @@ export const cleanMonitor = ({ heartbeats = [], cert, ...monitor }) => ({
notificationType: monitor.notificationType,
uptimePercentage: monitor.uptimePercentage,
averageHeartbeatLatency: monitor.averageHeartbeatLatency,
showFilters: monitor.showFilters || false,
showFilters: monitor.showFilters == '1',
paused: monitor.paused == '1',
cert: !cert?.isValid ? cert : cleanCertificate(cert),
heartbeats,
});
4 changes: 2 additions & 2 deletions server/class/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const cleanNotification = (notification) => ({
token: notification.token,
email: notification.email,
friendlyName: notification.friendlyName,
isEnabled: notification.isEnabled ? true : false,
isEnabled: notification.isEnabled == '1',
data:
typeof notification.data === 'string'
? parseJson(notification.data)
Expand All @@ -35,6 +35,6 @@ export const stringifyNotification = (notification) => ({
token: notification.token,
email: notification.email,
friendlyName: notification.friendlyName,
isEnabled: notification.isEnabled ? true : false,
isEnabled: notification.isEnabled == '1',
data: stringifyJson(notification.data),
});
Loading

0 comments on commit fcc14a4

Please sign in to comment.