Skip to content

Commit

Permalink
add load connections form (#433)
Browse files Browse the repository at this point in the history
* add load connections form

* add async functions to better handle loading status

* new connection dropdown, fix preserving connection settings, set source in edit, style fixes

* lint

* lint

* lint

* lint
  • Loading branch information
calebdoxsey authored Jan 24, 2025
1 parent b5b827a commit 9191c58
Show file tree
Hide file tree
Showing 21 changed files with 3,061 additions and 1,546 deletions.
11 changes: 7 additions & 4 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
module.exports = {
extends: [
'erb',
'plugin:@typescript-eslint/recommended',
],
extends: ['erb', 'plugin:@typescript-eslint/recommended'],
rules: {
// A temporary hack related to IDE not resolving correct package.json
'import/no-extraneous-dependencies': 'off',
Expand All @@ -24,6 +21,12 @@ module.exports = {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-this-alias': 'off',
'@typescript-eslint/no-var-requires': 'off',
// because of how we do ipc we need to save routes one at a time, awaiting in a loop makes sense
'no-await-in-loop': 'off',
// continue is fine
'no-continue': 'off',
// eslint doesn't seem to understand for ... of
'no-restricted-syntax': 'off',
},
parserOptions: {
ecmaVersion: 2020,
Expand Down
36 changes: 35 additions & 1 deletion api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ service Config {
rpc Export(ExportRequest) returns (ConfigData);
// Import imports previously serialized records
rpc Import(ImportRequest) returns (ImportResponse);
// FetchRoutes fetches all the routes from the routes portal.
rpc FetchRoutes(FetchRoutesRequest) returns (FetchRoutesResponse);
}

// Record represents a single tunnel record in the configuration
Expand All @@ -28,6 +30,7 @@ message Record {
repeated string tags = 2;
// connection data may be omitted if i.e. just manipulating the tags data
optional Connection conn = 3;
optional string source = 4;
}

message Records { repeated Record records = 1; }
Expand Down Expand Up @@ -85,7 +88,8 @@ service Listener {
// StatusUpdates opens a stream to listen to connection status updates
// a client has to subscribe and continuously
// listen to the broadcasted updates
rpc StatusUpdates(StatusUpdatesRequest) returns (stream ConnectionStatusUpdate);
rpc StatusUpdates(StatusUpdatesRequest)
returns (stream ConnectionStatusUpdate);
}

message ListenerUpdateRequest {
Expand All @@ -104,6 +108,28 @@ message ListenerStatusResponse { map<string, ListenerStatus> listeners = 1; }

message StatusUpdatesRequest { string connection_id = 1; }

message FetchRoutesRequest {
string server_url = 1;
oneof tls_options {
bool disable_tls_verification = 2;
bytes ca_cert = 3;
}
optional Certificate client_cert = 4;
optional ClientCertFromStore client_cert_from_store = 5;
}

message FetchRoutesResponse { repeated PortalRoute routes = 1; }

message PortalRoute {
string id = 1;
string name = 2;
string type = 3;
string from = 4;
string description = 5;
optional string connect_command = 6;
string logo_url = 7;
}

// ConnectionStatusUpdates represent connection state changes
message ConnectionStatusUpdate {
// record this event relates to
Expand Down Expand Up @@ -211,10 +237,18 @@ message ClientCertFromStore {
optional string subject_filter = 2;
}

enum Protocol {
UNKNOWN = 0;
TCP = 1;
UDP = 2;
}

// Connection
message Connection {
// name is a user friendly connection name that a user may define
optional string name = 1;
// the protocol to use for the connection
optional Protocol protocol = 10;
// remote_addr is a remote pomerium host:port
string remote_addr = 2;
// listen_address, if not provided, will assign a random port each time
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@
"@emotion/styled": "^11.11.5",
"@fontsource/dm-sans": "^5.0.20",
"@grpc/grpc-js": "^1.9.15",
"@mui/icons-material": "^6.4.1",
"@mui/lab": "^5.0.0-alpha.161",
"@mui/material": "^5.15.5",
"@mui/styles": "^5.15.5",
Expand All @@ -291,6 +292,7 @@
"source-map-support": "^0.5.19",
"ts-proto": "^1.166.2",
"typescript-eslint": "^0.0.1-alpha.0",
"usehooks-ts": "^3.1.0",
"validator": "^13.11.0"
},
"devEngines": {
Expand Down
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import SnackbarCloseButton from './renderer/components/SnackbarCloseButton';
import ConnectForm from './renderer/pages/ConnectForm';
import ConnectionView from './renderer/pages/ConnectionView';
import Layout from './renderer/pages/Layout';
import LoadForm from './renderer/pages/LoadForm';
import ManageConnections from './renderer/pages/ManageConnections';
import { THEMES } from './shared/constants';
import createCustomTheme, { ThemeConfig } from './shared/theme';
Expand Down Expand Up @@ -74,6 +75,7 @@ const App: FC = () => {
element={<Navigate to="/manage" replace />}
/>
<Route path="/manage" element={<ManageConnections />} />
<Route path="/loadForm" element={<LoadForm />} />
<Route
path="/view_connection/:connectionID"
element={<ConnectionView />}
Expand Down
21 changes: 13 additions & 8 deletions src/main.dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ import {
ExportFile,
LISTENER_LOG,
GET_ALL_RECORDS,
FETCH_ROUTES,
GetRecordsResponseArgs,
} from './shared/constants';
import {
ConnectionStatusUpdate,
Expand All @@ -57,6 +59,7 @@ import {
Record as ListenerRecord,
Selector,
StatusUpdatesRequest,
FetchRoutesRequest,
} from './shared/pb/api';
import Helper from './trayMenu/helper';

Expand Down Expand Up @@ -175,10 +178,8 @@ async function init(): Promise<void> {
ipcMain.on(GET_RECORDS, (evt, selector: Selector) => {
const sendTo = evt?.sender ? evt.sender : mainWindow?.webContents;
configClient.list(selector, (err, res) => {
sendTo?.send(GET_RECORDS, {
err,
res,
});
const args: GetRecordsResponseArgs = { err, res };
sendTo?.send(GET_RECORDS, args);
if (!err && res) {
if (selector.all) {
trayMenuHelper.setRecords(res.records);
Expand All @@ -200,10 +201,8 @@ async function init(): Promise<void> {
tags: [],
} as Selector,
(err, res) => {
sendTo?.send(GET_ALL_RECORDS, {
err,
res,
});
const args: GetRecordsResponseArgs = { err, res };
sendTo?.send(GET_ALL_RECORDS, args);
if (!err && res) {
trayMenuHelper.setRecords(res.records);
menu.tray.setContextMenu(trayMenuHelper.createContextMenu());
Expand Down Expand Up @@ -337,6 +336,12 @@ async function init(): Promise<void> {
// empty function otherwise causes fatal error on cancel !!!
updateStream.on('error', () => {});
});
ipcMain.on(FETCH_ROUTES, (evt, args) => {
const sendTo = evt?.sender ? evt.sender : mainWindow?.webContents;
configClient.fetchRoutes(args as FetchRoutesRequest, (err, res) => {
sendTo?.send(FETCH_ROUTES, { err, res });
});
});
menu.app.on('web-contents-created', () => {
contextMenu();
});
Expand Down
39 changes: 39 additions & 0 deletions src/renderer/components/AdvancedSettingsAccordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
Accordion,
AccordionDetails,
AccordionSummary,
Typography,
} from '@mui/material';
import React, { FC } from 'react';
import { ChevronDown } from 'react-feather';

export type AdvancedSettingsAccordionProps = React.PropsWithChildren;
const AdvancedSettingsAccordion: FC<AdvancedSettingsAccordionProps> = ({
children,
}) => {
return (
<Accordion
sx={{
backgroundColor: 'background.paper',
marginTop: 2,
paddingLeft: 2,
paddingRight: 2,
borderRadius: 4,
'&:before': {
display: 'none',
},
}}
square={false}
>
<AccordionSummary
expandIcon={<ChevronDown />}
aria-controls="advanced-settings-content"
id="advanced-settings-header"
>
<Typography variant="h5">Advanced Settings</Typography>
</AccordionSummary>
<AccordionDetails>{children}</AccordionDetails>
</Accordion>
);
};
export default AdvancedSettingsAccordion;
120 changes: 120 additions & 0 deletions src/renderer/components/NewConnectionButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* eslint-disable react/jsx-props-no-spreading */
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
import {
Button,
ClickAwayListener,
Grow,
MenuItem,
MenuList,
Paper,
Popper,
Stack,
Typography,
} from '@mui/material';
import React, { FC, useRef } from 'react';
import { useNavigate } from 'react-router-dom';

import usePopover from '../hooks/use-popover';

const NewConnectionButton: FC = () => {
const popover = usePopover<HTMLButtonElement>();
const navigate = useNavigate();
const anchorRef = useRef<HTMLDivElement>(null);

const handleClose = (event: Event) => {
if (
anchorRef.current &&
anchorRef.current.contains(event.target as HTMLElement)
) {
return;
}
popover.handleClose();
};

const handleMenuItemClick = (key: string) => {
popover.handleClose();
switch (key) {
case 'load':
navigate(`/loadForm`, {
replace: true,
});
break;
case 'add':
navigate(`/connectForm`, {
replace: true,
});
break;
default:
break;
}
};

const options = [
{
key: 'load',
title: 'Load Connections',
},
{
key: 'add',
title: 'Add Connecton',
},
];

return (
<>
<Button
variant="contained"
color="primary"
ref={popover.anchorRef}
onClick={popover.handleOpen}
startIcon={<AddCircleOutlineIcon />}
>
<Typography variant="button">New Connection</Typography>
</Button>
<Popper
sx={{ zIndex: 1 }}
open={popover.open}
anchorEl={popover.anchorRef.current}
role={undefined}
transition
disablePortal
placement="bottom-end"
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id="split-button-menu" autoFocusItem disablePadding>
{options.map((option) => (
<MenuItem
key={option.key}
onClick={() => handleMenuItemClick(option.key)}
sx={{ borderRadius: 1 }}
divider
>
<Stack
direction="row"
spacing={1}
justifyContent="center"
>
<Typography variant="button">{option.title}</Typography>
</Stack>
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</>
);
};

export default NewConnectionButton;
Loading

0 comments on commit 9191c58

Please sign in to comment.