Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.7.0 - Adds support for PostgreSQL and new setup system #59

Merged
merged 6 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
- Customizable themes/colors
- Customizable user profiles
- Support for notifications
- Discord
- Slack
- Telegram
- Webhook
- More to come...

## 🚀 Getting Started

Expand Down Expand Up @@ -86,6 +91,9 @@ pm2 monit
- [ ] Move to Oauth2 for authentication
- [ ] Allow session management (Track/logout from sessions)
- [ ] Add support for multiple databases
- [x] Add support for PostgreSQL
- [x] Add support for SQLite
- [ ] Add support for MongoDB
- [ ] Add support for more notification services
- [ ] Better role based access control

Expand Down
2 changes: 1 addition & 1 deletion app/components/home/menu/layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const MenuLayoutDropdown = () => {

return (
<Dropdown.Container
position="center"
position="left"
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
id="home-menu-layout"
Expand Down
6 changes: 4 additions & 2 deletions app/components/monitor/graph/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ const MonitorGraph = ({ monitor }) => {
useGraphStatus(monitor);

const data = statusHeartbeats.map((heartbeat = {}) => {
return { Latency: heartbeat.latency, time: heartbeat.date };
return {
Latency: heartbeat.latency,
time: dayjs(heartbeat.date).format(timeformat),
};
});

const gridColor =
Expand All @@ -42,7 +45,6 @@ const MonitorGraph = ({ monitor }) => {
dataKey="time"
style={{ fill: 'var(--accent-200)' }}
tick={{ fontSize: 12 }}
tickFormatter={(date) => dayjs(date).format(timeformat)}
/>
<YAxis
label={{
Expand Down
4 changes: 2 additions & 2 deletions app/components/register/email.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import PropTypes from 'prop-types';

// import local files
import TextInput from '../../components/ui/input';
import ProgressBar from '../../components/ui/progress';
import TextInput from '../ui/input';
import ProgressBar from '../ui/progress';

const RegisterEmailForm = ({
handleInput,
Expand Down
4 changes: 2 additions & 2 deletions app/components/register/password.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import PropTypes from 'prop-types';

// import local files
import TextInput from '../../components/ui/input';
import ProgressBar from '../../components/ui/progress';
import TextInput from '../ui/input';
import ProgressBar from '../ui/progress';
import RegisterChecklist from './checklist';

const RegisterPasswordForm = ({
Expand Down
2 changes: 1 addition & 1 deletion app/components/register/verify.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { toast } from 'react-toastify';
import { useNavigate } from 'react-router-dom';

// import local files
import ProgressBar from '../../components/ui/progress';
import ProgressBar from '../ui/progress';
import { createGetRequest } from '../../services/axios';

const RegisterVerify = () => {
Expand Down
161 changes: 161 additions & 0 deletions app/components/setup/database.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// import dependencies
import PropTypes from 'prop-types';

// import local files
import TextInput from '../ui/input';
import Dropdown from '../ui/dropdown';
import useDropdown from '../../hooks/useDropdown';
import useSetupFormContext from '../../hooks/useSetup';
import setupValidators from '../../../shared/validators/setup';

const databaseTypes = {
'better-sqlite3': { title: 'SQLite', img: 'sqlite', value: 'better-sqlite3' },
pg: { title: 'PostgreSQL', img: 'postgresql', value: 'pg' },
};

const pgInputs = [
{
id: 'postgresHost',
label: 'Postgres host',
type: 'text',
placeholder: 'localhost',
},
{
id: 'postgresPort',
label: 'Postgres port',
type: 'text',
placeholder: '5432',
},
{
id: 'postgresUser',
label: 'Postgres user',
type: 'text',
placeholder: 'postgres',
},
{
id: 'postgresPassword',
label: 'Postgres password',
type: 'password',
placeholder: 'Strong-Password',
},
];

const DropdownItem = ({ title, img, value, handleInput, inputs }) => {
return (
<Dropdown.Item
showDot
dotColor="primary"
onClick={() => handleInput(value)}
isSelected={inputs['databaseType'] === value}
>
<img
src={`/logo/${img}.svg`}
alt={title}
style={{ width: '20px', height: '20px' }}
/>
<div>{title}</div>
</Dropdown.Item>
);
};

DropdownItem.displayName = 'DropdownItem';

DropdownItem.propTypes = {
title: PropTypes.string.isRequired,
img: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
handleInput: PropTypes.func.isRequired,
inputs: PropTypes.object.isRequired,
};

const SetupDatabaseForm = () => {
const { handleInput, inputs, errors, setErrors } = useSetupFormContext();
const { dropdownIsOpen, toggleDropdown } = useDropdown(false, 'SQLite');

const changeDatabaseType = (value) => {
handleInput({ target: { id: 'databaseType', value } });
toggleDropdown();
};

return (
<>
<label className="input-label">Database</label>
<Dropdown.Container
position="center"
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
id="setup-database-status"
>
<Dropdown.Trigger
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
asInput
>
{!inputs['databaseType'] ? 'Select Database' : null}
{inputs['databaseType'] && databaseTypes[inputs['databaseType']] ? (
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
<img
src={`/logo/${databaseTypes[inputs['databaseType']].img}.svg`}
alt={databaseTypes[inputs['databaseType']].title}
style={{ width: '20px', height: '20px' }}
/>
<div>{databaseTypes[inputs['databaseType']].title}</div>
</div>
) : null}
</Dropdown.Trigger>

<Dropdown.List isOpen={dropdownIsOpen} fullWidth>
{Object.values(databaseTypes).map((type) => (
<DropdownItem
id="databaseType"
key={type.value}
title={type.title}
img={type.img}
value={type.value}
handleInput={changeDatabaseType}
inputs={inputs}
/>
))}
</Dropdown.List>
</Dropdown.Container>
<TextInput
type="text"
id="databaseName"
label="Database name"
error={errors['databaseName']}
value={inputs['databaseName'] || ''}
onChange={handleInput}
onBlur={(e) => {
const nameValidator = setupValidators['databaseName'];
if (nameValidator) {
nameValidator(e.target.value, setErrors);
}
}}
placeholder="Lunalytics"
/>

{inputs['databaseType'] === 'pg'
? pgInputs.map((input) => {
const validator = setupValidators[input.id];

return (
<TextInput
{...input}
key={input.id}
error={errors[input.id]}
onChange={handleInput}
value={inputs[input.id] || ''}
onBlur={(e) => validator(e.target.value, setErrors)}
/>
);
})
: null}
</>
);
};

SetupDatabaseForm.displayName = 'SetupDatabaseForm';

SetupDatabaseForm.propTypes = {};

export default SetupDatabaseForm;
61 changes: 61 additions & 0 deletions app/components/setup/dropdown.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// import dependencies
import PropTypes from 'prop-types';

// import local files
import useDropdown from '../../hooks/useDropdown';
import useSetupFormContext from '../../hooks/useSetup';
import Dropdown from '../ui/dropdown';

const SetupDropdown = ({ id, options, label = 'Dropdown' }) => {
const { handleInput, inputs } = useSetupFormContext();
const { dropdownIsOpen, toggleDropdown } = useDropdown(false, 'Automatic');
const value = inputs[id] || 'automatic';

return (
<>
<label className="input-label">{label}</label>
<Dropdown.Container
position="center"
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
id={`setup-dropdown-${id}`}
>
<Dropdown.Trigger
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
asInput
>
{value?.charAt(0)?.toUpperCase() + value?.slice(1) ||
'Select from dropdown'}
</Dropdown.Trigger>

<Dropdown.List isOpen={dropdownIsOpen} fullWidth>
{Object.values(options).map((item) => (
<Dropdown.Item
showDot
key={item}
dotColor="primary"
isSelected={value === item}
onClick={() => {
handleInput({ target: { id, value: item } });
toggleDropdown();
}}
>
{item.charAt(0).toUpperCase() + item.slice(1)}
</Dropdown.Item>
))}
</Dropdown.List>
</Dropdown.Container>
</>
);
};

SetupDropdown.displayName = 'SetupDropdown';

SetupDropdown.propTypes = {
id: PropTypes.string.isRequired,
options: PropTypes.object.isRequired,
label: PropTypes.string,
};

export default SetupDropdown;
Loading
Loading