Skip to content

Commit

Permalink
Merge pull request #65 from KSJaay/0.7.3
Browse files Browse the repository at this point in the history
0.7.3 - Cleans up codebase, adds support for headers and body for http monitors
  • Loading branch information
KSJaay authored Dec 24, 2024
2 parents fdca745 + be1cdee commit deaf81d
Show file tree
Hide file tree
Showing 25 changed files with 280 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
# production
/dist
/stats
/server/database/sqlite/*.db
/app/pages/icons.jsx
/app/components/icons/list.jsx
docs/.vitepress/dist
docs/.vitepress/cache
public/kanban.json
/logs
*.db

# misc
.DS_Store
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<a href="https://discord.gg/cjbGmmNdcd">Support</a>
</div>

![Demo](https://raw.githubusercontent.com/KSJaay/Lunalytics/refs/heads/main/docs/public/demo.gif)

## ⭐ Features

- Easy to self-host
Expand Down
26 changes: 22 additions & 4 deletions app/components/modal/monitor/configure.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import MonitorPageNotification from './pages/notification';
import useMonitorForm from '../../../hooks/useMonitorForm';
import { Accordion, AccordionItem } from '../../ui/accordion';
import MonitorHttpStatusCodes from './pages/http/statusCodes';
import MonitorHttpHeaders from './pages/headers';
import MonitorHttpBody from './pages/body';

const MonitorConfigureModal = ({
closeModal,
Expand Down Expand Up @@ -41,21 +43,21 @@ const MonitorConfigureModal = ({
isEdit={isEdit}
/>

{inputs.type === 'http' && (
{inputs.type === 'http' ? (
<MonitorPageHttp
inputs={inputs}
errors={errors}
handleInput={handleInput}
/>
)}
) : null}

{inputs.type === 'tcp' && (
{inputs.type === 'tcp' ? (
<MonitorPageTcp
inputs={inputs}
errors={errors}
handleInput={handleInput}
/>
)}
) : null}
<br />
<Accordion dark>
<AccordionItem
Expand Down Expand Up @@ -95,6 +97,22 @@ const MonitorConfigureModal = ({
handleInput={handleInput}
/>

{inputs.type === 'http' ? (
<>
<MonitorHttpHeaders
inputs={inputs}
errors={errors}
handleInput={handleInput}
/>

<MonitorHttpBody
inputs={inputs}
errors={errors}
handleInput={handleInput}
/>
</>
) : null}

<br />
</AccordionItem>
</Accordion>
Expand Down
43 changes: 43 additions & 0 deletions app/components/modal/monitor/pages/body.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import PropTypes from 'prop-types';
import Textarea from '../../../ui/textarea';

const MonitorHttpBody = ({ inputs, errors, handleInput }) => {
const parseBody = (body) => {
try {
if (typeof body === 'object') {
return JSON.stringify(body, null, 2);
}
} catch (error) {
return '';
}
};

return (
<>
<label className="input-label">HTTP Body</label>
<div style={{ padding: '0 0 8px 4px' }}>
Add a request body to be sent with the request. Make sure to follow JSON
key/value format.
</div>

<Textarea
rows={8}
error={errors.body}
onChange={(e) => handleInput('body', e.target.value)}
id="http-body-textarea"
>
{parseBody(inputs.body)}
</Textarea>
</>
);
};

MonitorHttpBody.displayName = 'MonitorHttpBody';

MonitorHttpBody.propTypes = {
inputs: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired,
handleInput: PropTypes.func.isRequired,
};

export default MonitorHttpBody;
43 changes: 43 additions & 0 deletions app/components/modal/monitor/pages/headers.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import PropTypes from 'prop-types';
import Textarea from '../../../ui/textarea';

const MonitorHttpHeaders = ({ inputs, errors, handleInput }) => {
const parseHeaders = (body) => {
try {
if (typeof body === 'object') {
return JSON.stringify(body, null, 2);
}
} catch (error) {
return '';
}
};

return (
<>
<label className="input-label">HTTP Headers</label>
<div style={{ padding: '0 0 8px 4px' }}>
Add additional headers to be sent with the request. Make sure to follow
JSON key/value format.
</div>

<Textarea
rows={8}
error={errors.headers}
onChange={(e) => handleInput('headers', e.target.value)}
id="http-headers-textarea"
>
{parseHeaders(inputs.headers)}
</Textarea>
</>
);
};

MonitorHttpHeaders.displayName = 'MonitorHttpHeaders';

MonitorHttpHeaders.propTypes = {
inputs: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired,
handleInput: PropTypes.func.isRequired,
};

export default MonitorHttpHeaders;
4 changes: 2 additions & 2 deletions app/components/settings/ui/tab/tab.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
border-radius: pxToRem(12);

&:hover {
background-color: var(--accent-900);
background-color: var(--accent-800);
}

&.active {
background-color: var(--accent-900);
background-color: var(--accent-800);
color: var(--primary-500);
}
}
Expand Down
4 changes: 2 additions & 2 deletions app/components/ui/textarea.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import PropTypes from 'prop-types';
const Textarea = ({ label, error, id = 'text-input', children, ...props }) => {
return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
{label && <label>{label}</label>}
{label && <label className="input-label">{label}</label>}
<textarea className="textarea" id={id} {...props}>
{children}
</textarea>
{error && (
<span className="input-error" id={`textarea-error-${props.id}`}>
<span className="input-error" id={`textarea-error-${id}`}>
{error}
</span>
)}
Expand Down
23 changes: 23 additions & 0 deletions app/handlers/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ import { toast } from 'react-toastify';
// import local files
import { createPostRequest } from '../services/axios';

const parseJson = (str) => {
try {
if (typeof str === 'string') {
return JSON.parse(str);
}
if (typeof str === 'object') {
if (Array.isArray(str)) return {};
return str;
}

return {};
} catch (e) {
return {};
}
};

const handleMonitor = async (form, isEdit, closeModal, setMonitor) => {
try {
const apiPath = isEdit ? '/api/monitor/edit' : '/api/monitor/add';
Expand All @@ -21,8 +37,13 @@ const handleMonitor = async (form, isEdit, closeModal, setMonitor) => {
monitorId,
notificationId,
notificationType,
headers,
body,
} = form;

const parsedHeaders = parseJson(headers);
const parsedBody = parseJson(body);

const query = await createPostRequest(apiPath, {
name,
type,
Expand All @@ -36,6 +57,8 @@ const handleMonitor = async (form, isEdit, closeModal, setMonitor) => {
monitorId,
notificationId,
notificationType,
headers: parsedHeaders,
body: parsedBody,
});

setMonitor(query.data);
Expand Down
14 changes: 14 additions & 0 deletions docs/internals/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Previous updates

## 0.7.3

### Cleans up codebase, adds support for headers and body for http monitors

### Summary

I thought I had added support for headers and body for http monitors already, but I totally forgot to add it to the UI. This was bought up in https://github.com/KSJaay/Lunalytics/issues/62. This update adds support for headers and body for http monitors, along with some other small changes.

### Updates

- Adds support for headers and body for http monitors
- Cleans up monitor codebase to make things easier to read
- Adds gif showing UI on the README

## 0.7.2

### Cleans up codebase, adds support for cloning and pausing monitors
Expand Down
Binary file added docs/public/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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.2",
"version": "0.7.3",
"description": "Open source Node.js server/website monitoring tool",
"private": true,
"author": "KSJaay <ksjaay@gmail.com>",
Expand Down
7 changes: 5 additions & 2 deletions server/cache/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
updateCertificate,
} from '../database/queries/certificate.js';
import { fetchNotificationById } from '../database/queries/notification.js';
import { cleanPartialMonitor } from '../class/monitor.js';

class Master {
constructor() {
Expand Down Expand Up @@ -48,16 +49,18 @@ class Master {
}

async checkStatus(monitorId) {
const monitor = await fetchMonitor(monitorId).catch(() => false);
const query = await fetchMonitor(monitorId).catch(() => false);

if (!monitor) {
if (!query) {
clearTimeout(this.timeouts.get(monitorId));
this.timeouts.delete(monitorId);
await deleteCertificate(monitorId);
await deleteHeartbeats(monitorId);
return;
}

const monitor = cleanPartialMonitor(query);

if (monitor.paused) return;

if (this.timeouts.has(monitorId)) {
Expand Down
25 changes: 21 additions & 4 deletions server/class/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ const parseJsonStatus = (str) => {
}
};

const parseJson = (str) => {
try {
if (!str) return {};
if (typeof str === 'string') {
return JSON.parse(str);
}
if (typeof str === 'object') {
if (Array.isArray(str)) return {};
return str;
}

return {};
} catch (e) {
return {};
}
};

export const cleanPartialMonitor = (monitor) => ({
monitorId: monitor.monitorId,
name: monitor.name,
Expand All @@ -16,8 +33,8 @@ export const cleanPartialMonitor = (monitor) => ({
retryInterval: parseInt(monitor.retryInterval),
requestTimeout: parseInt(monitor.requestTimeout),
method: monitor.method,
headers: monitor.headers,
body: monitor.body,
headers: parseJson(monitor.headers),
body: parseJson(monitor.body),
valid_status_codes: parseJsonStatus(monitor.valid_status_codes),
email: monitor.email,
type: monitor.type,
Expand All @@ -38,8 +55,8 @@ export const cleanMonitor = ({ heartbeats = [], cert, ...monitor }) => ({
retryInterval: parseInt(monitor.retryInterval),
requestTimeout: parseInt(monitor.requestTimeout),
method: monitor.method,
headers: monitor.headers,
body: monitor.body,
headers: parseJson(monitor.headers),
body: parseJson(monitor.body),
valid_status_codes: parseJsonStatus(monitor.valid_status_codes),
email: monitor.email,
type: monitor.type,
Expand Down
14 changes: 14 additions & 0 deletions server/middleware/monitor/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ import { createMonitor } from '../../database/queries/monitor.js';
import { fetchHeartbeats } from '../../database/queries/heartbeat.js';
import { fetchCertificate } from '../../database/queries/certificate.js';

const stringifyJson = (obj) => {
try {
if (typeof obj === 'string') {
return obj;
}

return JSON.stringify(obj);
} catch (e) {
return '{}';
}
};

export const getTcpOrHttpData = (body, email, isHttp) => {
let monitor = {
name: body.name,
Expand All @@ -27,6 +39,8 @@ export const getTcpOrHttpData = (body, email, isHttp) => {
...monitor,
method: body.method,
valid_status_codes: JSON.stringify(body.valid_status_codes),
headers: stringifyJson(body.headers),
body: stringifyJson(body.body),
type: 'http',
};
} else {
Expand Down
Loading

0 comments on commit deaf81d

Please sign in to comment.