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

add load connections form #433

Merged
merged 7 commits into from
Jan 24, 2025
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
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
Loading