Move Teleport Connect over to Vite (#36921)

* Move Teleport connect over to Vite

* Clean up the Electron Vite config

* Add mts to prettier/eslint

* Add license headers

* Dedupe @babel scope

* Remove console.log from mainProcess

* Set suggestedReviewers on CreateAccessRequestRequest

* Remove unneeded code added from strict null checks

* Move out dir to build/app

On masted it's build/app/dist.

We don't want to use build directly, as the app is being packaged in
build/release.

* Move Node-only deps to dependencies

https://electron-vite.org/guide/dev#dependencies-vs-devdependencies

cross-env has been removed since it's not needed at all.

* Remove eval warning suppression

* Fix default path of tsh

* Rename dev-term to start-term-skip-native-deps

* Use correct `oneofKind` value for `ptyEventOneOfIsStartError`

* Undo strict null check changes

* Correctly construct env vars struct for shell

Based on https://stackoverflow.com/a/66989401

* Allow reloading renderer app

* Move JSDoc for TshdEventContextBridgeService to correct position

* Remove outdated comment about type casting in tshdEvents/index.ts

* Drop type cast for error.cause['message']

* Ignore teleterm's build dir in .eslintignore

**/dist/** has been removed from ignorePatterns because it's no longer
necessary, neither Web UI nor Connect build in a dir matching this pattern.

* Define NODE_ENV for main and renderer

* Define only NODE_ENV, not whole process.env

Otherwise process.env in the main process is always replaced with an
object containing just NODE_ENV.

We need process.env to be properly populated since it's then used when
spawning tsh daemon and other processes.

Using define like this guarantees that only callsites using
process.env.NODE_ENV directly will be affected.

* Add support for the cluster preferences backend mismatch from the gRPC UserPreferences object

* Return resourceIds from preferences, convert preferences before updating

* Future proof all of the clusterPreferences object getting converted correctly

* Fix cluster preferences for leaf clusters

* Use correct `AssistViewMode` enum, remove the old one and unused interface

* Lint fix

* Add back in JS protobuf stuff, it can be removed in a later PR

* Remove `List` from property

* Add license header

* Remove console.logs

---------

Co-authored-by: Rafał Cieślak <[email protected]>
Co-authored-by: Grzegorz Zdunek <[email protected]>
This commit is contained in:
Ryan Clark
2024-01-26 15:47:33 +00:00
committed by GitHub
co-authored by Rafał Cieślak Grzegorz Zdunek
parent 8360fc5e25
commit 200553c474
108 changed files with 3653 additions and 4374 deletions
+2
View File
@@ -3,3 +3,5 @@
# Ignore WASM generated files:
web/packages/teleport/src/ironrdp/pkg
web/packages/teleterm/build
+4 -3
View File
@@ -10,6 +10,7 @@
"start-teleport-e": "yarn workspace @gravitational/teleport.e start",
"build-term": "yarn workspace @gravitational/teleterm build",
"start-term": "yarn workspace @gravitational/teleterm start",
"start-term-skip-native-deps": "yarn workspace @gravitational/teleterm start-skip-native-deps",
"package-term": "yarn workspace @gravitational/teleterm package",
"storybook": "start-storybook -p 9002 -c web/.storybook -s web/.storybook/public",
"storybook-smoke-test": "yarn storybook --ci --smoke-test",
@@ -18,10 +19,10 @@
"test-update-snapshot": "yarn test -- --updateSnapshot",
"tdd": "jest --watch",
"lint": "yarn prettier-check && yarn eslint",
"eslint": "eslint --quiet '+(e|web)/**/*.{ts,tsx,js,jsx}'",
"eslint": "eslint --quiet '+(e|web)/**/*.{ts,tsx,js,jsx,mts}'",
"type-check": "NODE_OPTIONS='--max-old-space-size=4096' tsc --noEmit",
"prettier-check": "yarn prettier --check '+(e|web)/**/*.{ts,tsx,js,jsx}'",
"prettier-write": "yarn prettier --write --loglevel silent '+(e|web)/**/*.{ts,tsx,js,jsx}'",
"prettier-check": "yarn prettier --check '+(e|web)/**/*.{ts,tsx,js,jsx,mts}'",
"prettier-write": "yarn prettier --write --loglevel silent '+(e|web)/**/*.{ts,tsx,js,jsx,mts}'",
"process-icons": "node web/packages/design/src/Icon/script/script.js & yarn prettier --loglevel silent --write 'web/packages/design/src/Icon/Icons/*.tsx'",
"nop": "exit 0"
},
+39
View File
@@ -0,0 +1,39 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import type { Plugin } from 'vite';
export function cspPlugin(csp: string): Plugin {
return {
name: 'teleport-connect-html-plugin',
transformIndexHtml(html) {
return {
html,
tags: [
{
tag: 'meta',
attrs: {
'http-equiv': 'Content-Security-Policy',
content: csp,
},
},
],
};
},
};
}
@@ -157,6 +157,7 @@ function createDefaultConfig() {
design: path.join(__dirname, '/../../design/src'),
shared: path.join(__dirname, '/../../shared'),
'gen-proto-js': path.join(__dirname, '/../../../../gen/proto/js'),
'gen-proto-ts': path.join(__dirname, '/../../../../gen/proto/ts'),
},
/*
@@ -24,19 +24,20 @@ import {
import { KeysEnum, storageService } from 'teleport/services/storageService';
import { ThemePreference } from 'teleport/services/userPreferences/types';
import cfg from 'teleport/config';
import { Theme } from 'gen-proto-ts/teleport/userpreferences/v1/theme_pb';
import { darkTheme, lightTheme, bblpTheme } from '../theme';
import { GlobalStyle } from './globals';
function themePreferenceToTheme(themePreference: ThemePreference) {
return themePreference === ThemePreference.Light ? lightTheme : darkTheme;
function themePreferenceToTheme(themePreference: Theme) {
return themePreference === Theme.LIGHT ? lightTheme : darkTheme;
}
const ThemeProvider = props => {
const [themePreference, setThemePreference] = useState<ThemePreference>(
const [themePreference, setThemePreference] = useState<Theme>(
storageService.getThemePreference()
);
@@ -33,7 +33,8 @@ import {
ArrowsOut,
} from 'design/Icon';
import { ViewMode } from 'shared/services/unifiedResourcePreferences';
import { ViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb';
import { HoverTooltip } from 'shared/components/ToolTip';
import { FilterKind } from './UnifiedResources';
@@ -132,7 +133,7 @@ export function FilterPanel({
<Flex mr={1}>{BulkActions}</Flex>
{!hideViewModeOptions && (
<>
{currentViewMode === ViewMode.VIEW_MODE_LIST && (
{currentViewMode === ViewMode.LIST && (
<ButtonBorder
size="small"
css={`
@@ -468,10 +469,8 @@ function ViewModeSwitch({
return (
<ViewModeSwitchContainer>
<ViewModeSwitchButton
className={
currentViewMode === ViewMode.VIEW_MODE_CARD ? 'selected' : ''
}
onClick={() => setCurrentViewMode(ViewMode.VIEW_MODE_CARD)}
className={currentViewMode === ViewMode.CARD ? 'selected' : ''}
onClick={() => setCurrentViewMode(ViewMode.CARD)}
css={`
border-right: 1px solid
${props => props.theme.colors.spotBackground[2]};
@@ -482,10 +481,8 @@ function ViewModeSwitch({
<SquaresFour size="small" color="text.main" />
</ViewModeSwitchButton>
<ViewModeSwitchButton
className={
currentViewMode === ViewMode.VIEW_MODE_LIST ? 'selected' : ''
}
onClick={() => setCurrentViewMode(ViewMode.VIEW_MODE_LIST)}
className={currentViewMode === ViewMode.LIST ? 'selected' : ''}
onClick={() => setCurrentViewMode(ViewMode.LIST)}
css={`
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
@@ -30,11 +30,11 @@ import { UrlResourcesParams } from 'teleport/config';
import { ResourcesResponse } from 'teleport/services/agents';
import {
UnifiedResourcePreferences,
DefaultTab,
ViewMode,
LabelsViewMode,
} from 'shared/services/unifiedResourcePreferences';
UnifiedResourcePreferences,
ViewMode,
} from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb';
import { makeErrorAttempt, makeProcessingAttempt } from 'shared/hooks/useAsync';
@@ -97,9 +97,9 @@ const story = ({
};
return () => {
const [userPrefs, setUserPrefs] = useState<UnifiedResourcePreferences>({
defaultTab: DefaultTab.DEFAULT_TAB_ALL,
viewMode: ViewMode.VIEW_MODE_CARD,
labelsViewMode: LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED,
defaultTab: DefaultTab.ALL,
viewMode: ViewMode.CARD,
labelsViewMode: LabelsViewMode.COLLAPSED,
});
const { fetch, attempt, resources } = useUnifiedResourcesFetch({
fetchFunc,
@@ -43,10 +43,11 @@ import { ResourcesResponse } from 'teleport/services/agents';
import {
DefaultTab,
ViewMode,
UnifiedResourcePreferences,
LabelsViewMode,
} from 'shared/services/unifiedResourcePreferences';
UnifiedResourcePreferences,
ViewMode,
} from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb';
import { HoverTooltip } from 'shared/components/ToolTip';
import {
makeEmptyAttempt,
@@ -90,11 +91,11 @@ export const PINNING_NOT_SUPPORTED_MESSAGE =
const tabs: { label: string; value: DefaultTab }[] = [
{
label: 'All Resources',
value: DefaultTab.DEFAULT_TAB_ALL,
value: DefaultTab.ALL,
},
{
label: 'Pinned Resources',
value: DefaultTab.DEFAULT_TAB_PINNED,
value: DefaultTab.PINNED,
},
];
@@ -304,7 +305,7 @@ export function UnifiedResources(props: UnifiedResourcesProps) {
};
const selectTab = (value: DefaultTab) => {
const pinnedOnly = value === DefaultTab.DEFAULT_TAB_PINNED;
const pinnedOnly = value === DefaultTab.PINNED;
setParams({
...params,
pinnedOnly,
@@ -366,8 +367,7 @@ export function UnifiedResources(props: UnifiedResourcesProps) {
};
const expandAllLabels =
unifiedResourcePreferences.labelsViewMode ===
LabelsViewMode.LABELS_VIEW_MODE_EXPANDED;
unifiedResourcePreferences.labelsViewMode === LabelsViewMode.EXPANDED;
useEffect(() => {
const resizeObserver = new ResizeObserver(entries => {
@@ -386,8 +386,7 @@ export function UnifiedResources(props: UnifiedResourcesProps) {
}, []);
const ViewComponent =
unifiedResourcePreferences.viewMode === ViewMode.VIEW_MODE_CARD ||
forceCardView
unifiedResourcePreferences.viewMode === ViewMode.CARD || forceCardView
? CardsView
: ListView;
@@ -448,9 +447,7 @@ export function UnifiedResources(props: UnifiedResourcesProps) {
ClusterDropdown={ClusterDropdown}
setExpandAllLabels={expandAllLabels => {
setLabelsViewMode(
expandAllLabels
? LabelsViewMode.LABELS_VIEW_MODE_EXPANDED
: LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED
expandAllLabels ? LabelsViewMode.EXPANDED : LabelsViewMode.COLLAPSED
);
}}
hideViewModeOptions={forceCardView}
@@ -498,14 +495,14 @@ export function UnifiedResources(props: UnifiedResourcesProps) {
key={tab.value}
onClick={() => selectTab(tab.value)}
disabled={
tab.value === DefaultTab.DEFAULT_TAB_PINNED &&
tab.value === DefaultTab.PINNED &&
pinning.kind === 'not-supported'
}
title={tab.label}
isSelected={
params.pinnedOnly
? tab.value === DefaultTab.DEFAULT_TAB_PINNED
: tab.value === DefaultTab.DEFAULT_TAB_ALL
? tab.value === DefaultTab.PINNED
: tab.value === DefaultTab.ALL
}
/>
))}
@@ -1,56 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Should be kept in sync with
// https://github.com/gravitational/teleport/blob/cc330931e2b691b7438bfee73587e828e874fa47/api/proto/teleport/userpreferences/v1/unified_resource_preferences.proto
//
// The exported enums and types have the same names and values/properties as
// the values generated by protogen.
// It allows using them interchangeably with the generated values.
//
// Ideally, we should use the generated values, but it was a challenge
// to make them work with Vite (the generated files are in CommonJS format);
// additionally, they increased the bundle size significantly.
/** Preferences related to the Unified Resource view. */
export interface UnifiedResourcePreferences {
/** Default tab selected in the unified resource view. */
defaultTab: DefaultTab;
/** View mode selected in the unified resource view (Cards/List). */
viewMode: ViewMode;
/** Labels view mode is whether the labels for resources should all be collapsed or expanded. This only applies to the list view. */
labelsViewMode: LabelsViewMode;
}
export enum DefaultTab {
DEFAULT_TAB_UNSPECIFIED = 0,
DEFAULT_TAB_ALL = 1,
DEFAULT_TAB_PINNED = 2,
}
export enum ViewMode {
VIEW_MODE_UNSPECIFIED = 0,
VIEW_MODE_CARD = 1,
VIEW_MODE_LIST = 2,
}
export enum LabelsViewMode {
LABELS_VIEW_MODE_UNSPECIFIED = 0,
LABELS_VIEW_MODE_EXPANDED = 1,
LABELS_VIEW_MODE_COLLAPSED = 2,
}
+25 -17
View File
@@ -20,6 +20,8 @@ import React, { useEffect, useState } from 'react';
import styled, { keyframes } from 'styled-components';
import { sharedStyles } from 'design/theme/themes/sharedStyles';
import { AssistViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/assist_pb';
import { Header } from 'teleport/Assist/Header';
import { ConversationHistory } from 'teleport/Assist/ConversationHistory';
import {
@@ -28,7 +30,6 @@ import {
} from 'teleport/Assist/context/AssistContext';
import { ConversationList } from 'teleport/Assist/ConversationList';
import { useLayout } from 'teleport/Main/LayoutContext';
import { ViewMode } from 'teleport/Assist/types';
import { Settings } from 'teleport/Assist/Settings';
import { ErrorBanner, ErrorList } from 'teleport/Assist/ErrorBanner';
import { useUser } from 'teleport/User/UserContext';
@@ -60,9 +61,9 @@ const slideIn = keyframes`
}
`;
function variables(props: { viewMode: ViewMode }) {
function variables(props: { viewMode: AssistViewMode }) {
switch (props.viewMode) {
case ViewMode.Popup:
case AssistViewMode.POPUP:
return {
'--assist-gutter': '20px',
'--assist-border-radius': '15px',
@@ -76,8 +77,8 @@ function variables(props: { viewMode: ViewMode }) {
'--assist-bottom-padding': '5px',
};
case ViewMode.PopupExpanded:
case ViewMode.PopupExpandedSidebarVisible:
case AssistViewMode.POPUP_EXPANDED:
case AssistViewMode.POPUP_EXPANDED_SIDEBAR_VISIBLE:
return {
'--assist-gutter': '20px',
'--assist-border-radius': '15px',
@@ -91,7 +92,7 @@ function variables(props: { viewMode: ViewMode }) {
'--assist-bottom-padding': '5px',
};
case ViewMode.Docked:
case AssistViewMode.DOCKED:
return {
'--assist-gutter': '0',
'--assist-border-radius': '0',
@@ -108,11 +109,11 @@ function variables(props: { viewMode: ViewMode }) {
}
function sidebarVariables(props: {
viewMode: ViewMode;
viewMode: AssistViewMode;
sidebarVisible: boolean;
}) {
switch (props.viewMode) {
case ViewMode.Popup:
case AssistViewMode.POPUP:
if (props.sidebarVisible) {
return {
'--conversation-width': '550px',
@@ -134,8 +135,8 @@ function sidebarVariables(props: {
'--conversation-list-position': 'absolute',
};
case ViewMode.PopupExpanded:
case ViewMode.PopupExpandedSidebarVisible:
case AssistViewMode.POPUP_EXPANDED:
case AssistViewMode.POPUP_EXPANDED_SIDEBAR_VISIBLE:
if (props.sidebarVisible) {
return {
'--conversation-list-margin': '0',
@@ -157,7 +158,7 @@ function sidebarVariables(props: {
'--conversation-list-position': 'absolute',
};
case ViewMode.Docked:
case AssistViewMode.DOCKED:
if (props.sidebarVisible) {
return {
'--conversation-width': `${dockedAssistWidth}px`,
@@ -301,11 +302,17 @@ function AssistContent(props: AssistProps) {
const { hasDockedElement, setHasDockedElement } = useLayout();
useEffect(() => {
if (!hasDockedElement && preferences.assist.viewMode === ViewMode.Docked) {
if (
!hasDockedElement &&
preferences.assist.viewMode === AssistViewMode.DOCKED
) {
setHasDockedElement(true);
}
if (hasDockedElement && preferences.assist.viewMode !== ViewMode.Docked) {
if (
hasDockedElement &&
preferences.assist.viewMode !== AssistViewMode.DOCKED
) {
setHasDockedElement(false);
}
}, [hasDockedElement, preferences.assist.viewMode]);
@@ -320,8 +327,8 @@ function AssistContent(props: AssistProps) {
}
if (
preferences.assist.viewMode === ViewMode.Popup ||
preferences.assist.viewMode === ViewMode.Docked
preferences.assist.viewMode === AssistViewMode.POPUP ||
preferences.assist.viewMode === AssistViewMode.DOCKED
) {
toggleSidebar(false);
}
@@ -333,7 +340,8 @@ function AssistContent(props: AssistProps) {
if (
sidebarVisible &&
preferences.assist.viewMode !== ViewMode.PopupExpandedSidebarVisible
preferences.assist.viewMode !==
AssistViewMode.POPUP_EXPANDED_SIDEBAR_VISIBLE
) {
toggleSidebar(false);
}
@@ -365,7 +373,7 @@ function AssistContent(props: AssistProps) {
</ErrorBanner>
));
const docked = preferences.assist.viewMode === ViewMode.Docked;
const docked = preferences.assist.viewMode === AssistViewMode.DOCKED;
return (
<Container onClick={handleClose} docked={docked}>
@@ -19,14 +19,15 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import { AssistViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/assist_pb';
import { useAssist } from 'teleport/Assist/context/AssistContext';
import { ConversationHistoryItem } from 'teleport/Assist/ConversationHistory/ConversationHistoryItem';
import { DeleteConversationDialog } from 'teleport/Assist/ConversationHistory/DeleteConversationDialog';
import { ViewMode } from 'teleport/Assist/types';
interface ConversationHistoryProps {
onConversationSelect: (id: string) => void;
viewMode: ViewMode;
viewMode: AssistViewMode;
onError: (message: string) => void;
}
@@ -58,10 +59,10 @@ const List = styled.ul.attrs({ 'data-scrollbar': 'default' })`
overflow-y: auto;
`;
function isExpanded(viewMode: ViewMode) {
function isExpanded(viewMode: AssistViewMode) {
return (
viewMode === ViewMode.PopupExpanded ||
viewMode === ViewMode.PopupExpandedSidebarVisible
viewMode === AssistViewMode.POPUP_EXPANDED ||
viewMode === AssistViewMode.POPUP_EXPANDED_SIDEBAR_VISIBLE
);
}
@@ -19,13 +19,14 @@
import React, { useEffect, useRef } from 'react';
import styled from 'styled-components';
import { AssistViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/assist_pb';
import { Conversation } from 'teleport/Assist/Conversation';
import { useAssist } from 'teleport/Assist/context/AssistContext';
import { MessageBox } from 'teleport/Assist/MessageBox';
import { ViewMode } from 'teleport/Assist/types';
interface ConversationListProps {
viewMode: ViewMode;
viewMode: AssistViewMode;
}
const Container = styled.div.attrs({ 'data-scrollbar': 'default' })`
@@ -26,13 +26,14 @@ import {
SettingsIcon,
} from 'design/SVGIcon';
import { AssistViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/assist_pb';
import { useAssist } from 'teleport/Assist/context/AssistContext';
import { Tooltip } from 'teleport/Assist/shared/Tooltip';
import { ViewMode } from 'teleport/Assist/types';
import { HeaderIcon } from 'teleport/Assist/shared';
interface HeaderProps {
viewMode: ViewMode;
viewMode: AssistViewMode;
sidebarVisible: boolean;
onClose: () => void;
onToggleSidebar: () => void;
@@ -19,14 +19,15 @@
import React from 'react';
import styled from 'styled-components';
import { AssistViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/assist_pb';
import logoLight from 'teleport/Navigation/logoLight.svg';
import logoDark from 'teleport/Navigation/logoDark.svg';
import { ViewMode } from 'teleport/Assist/types';
import { Description, Title } from 'teleport/Assist/Settings/shared';
interface DisplaySettingsProps {
viewMode: ViewMode;
onChange: (viewMode: ViewMode) => void;
viewMode: AssistViewMode;
onChange: (viewMode: AssistViewMode) => void;
}
const ViewModes = styled.div`
@@ -241,8 +242,8 @@ export function DisplaySettings(props: DisplaySettingsProps) {
<ViewModes>
<ViewModeContainer
active={props.viewMode === ViewMode.Popup}
onClick={() => props.onChange(ViewMode.Popup)}
active={props.viewMode === AssistViewMode.POPUP}
onClick={() => props.onChange(AssistViewMode.POPUP)}
>
<ViewModeExample>
<DimmedBackground />
@@ -256,8 +257,8 @@ export function DisplaySettings(props: DisplaySettingsProps) {
Popup
</ViewModeContainer>
<ViewModeContainer
active={props.viewMode === ViewMode.Docked}
onClick={() => props.onChange(ViewMode.Docked)}
active={props.viewMode === AssistViewMode.DOCKED}
onClick={() => props.onChange(AssistViewMode.DOCKED)}
>
<ViewModeExample>
<MockPage />
@@ -269,8 +270,8 @@ export function DisplaySettings(props: DisplaySettingsProps) {
Docked
</ViewModeContainer>
<ViewModeContainer
active={props.viewMode === ViewMode.PopupExpanded}
onClick={() => props.onChange(ViewMode.PopupExpanded)}
active={props.viewMode === AssistViewMode.POPUP_EXPANDED}
onClick={() => props.onChange(AssistViewMode.POPUP_EXPANDED)}
>
<ViewModeExample>
<DimmedBackground />
@@ -284,8 +285,12 @@ export function DisplaySettings(props: DisplaySettingsProps) {
Expanded popup
</ViewModeContainer>
<ViewModeContainer
active={props.viewMode === ViewMode.PopupExpandedSidebarVisible}
onClick={() => props.onChange(ViewMode.PopupExpandedSidebarVisible)}
active={
props.viewMode === AssistViewMode.POPUP_EXPANDED_SIDEBAR_VISIBLE
}
onClick={() =>
props.onChange(AssistViewMode.POPUP_EXPANDED_SIDEBAR_VISIBLE)
}
>
<ViewModeExample>
<DimmedBackground />
@@ -23,7 +23,11 @@ import { CloseIcon, DisplayIcon, TerminalIcon } from 'design/SVGIcon';
import Flex from 'design/Flex';
import { AssistUserPreferences, ViewMode } from 'teleport/Assist/types';
import {
AssistUserPreferences,
AssistViewMode,
} from 'gen-proto-ts/teleport/userpreferences/v1/assist_pb';
import { useAssist } from 'teleport/Assist/context/AssistContext';
import { Tooltip } from 'teleport/Assist/shared/Tooltip';
import { HeaderIcon } from 'teleport/Assist/shared';
@@ -40,7 +44,7 @@ interface SettingsProps {
onDebugMenuToggle: (enabled: boolean) => void;
}
const Container = styled.div<{ viewMode: ViewMode }>`
const Container = styled.div<{ viewMode: AssistViewMode }>`
position: fixed;
top: 0;
left: 0;
@@ -169,12 +173,12 @@ export function Settings(props: SettingsProps) {
savingTimeoutRef.current = window.setTimeout(() => setSaving(false), 500);
}
function handleChangeViewMode(viewMode: ViewMode) {
function handleChangeViewMode(viewMode: AssistViewMode) {
if (viewMode === preferences.assist.viewMode) {
return;
}
toggleSidebar(viewMode === ViewMode.PopupExpandedSidebarVisible);
toggleSidebar(viewMode === AssistViewMode.POPUP_EXPANDED_SIDEBAR_VISIBLE);
saveSettings({ viewMode });
}
-12
View File
@@ -275,15 +275,3 @@ export interface ExecuteRemoteCommandPayload {
labels?: { key: string; value: string }[];
nodes?: string[];
}
export enum ViewMode {
Docked = 1,
Popup = 2,
PopupExpanded = 3,
PopupExpandedSidebarVisible = 4,
}
export interface AssistUserPreferences {
preferredLogins: string[];
viewMode: ViewMode;
}
@@ -22,6 +22,8 @@ import { MemoryRouter } from 'react-router';
import { render, screen } from 'design/utils/testing';
import { Resource } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import TeleportContextProvider from 'teleport/TeleportContextProvider';
import { Discover } from 'teleport/Discover/Discover';
import { FeaturesContextProvider } from 'teleport/FeaturesContext';
@@ -40,8 +42,6 @@ import {
DATABASES_UNGUIDED_DOC,
} from 'teleport/Discover/SelectResource/databases';
import { ClusterResource } from 'teleport/services/userPreferences/types';
import { mockUserContextProviderWith } from 'teleport/User/testHelpers/mockUserContextWith';
import { makeTestUserContext } from 'teleport/User/testHelpers/makeTestUserContext';
@@ -55,7 +55,7 @@ beforeEach(() => {
type createProps = {
initialEntry?: string;
preferredResource?: ClusterResource;
preferredResource?: Resource;
};
const create = ({ initialEntry = '', preferredResource }: createProps) => {
@@ -113,7 +113,7 @@ test('displays all resources by default', () => {
test('location state applies filter/search', () => {
create({
initialEntry: 'desktop',
preferredResource: ClusterResource.RESOURCE_WEB_APPLICATIONS,
preferredResource: Resource.WEB_APPLICATIONS,
});
expect(screen.getAllByTestId(ResourceKind.Desktop)).toHaveLength(
@@ -19,6 +19,10 @@
import React from 'react';
import { MemoryRouter } from 'react-router';
import { Resource } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import { UserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/userpreferences_pb';
import {
createTeleportContext,
getAcl,
@@ -30,10 +34,6 @@ import { UserContext } from 'teleport/User/UserContext';
import { makeDefaultUserPreferences } from 'teleport/services/userPreferences/userPreferences';
import {
ClusterResource,
UserPreferences,
} from 'teleport/services/userPreferences/types';
import { Acl } from 'teleport/services/user';
import { SelectResource } from './SelectResource';
@@ -86,7 +86,7 @@ export const InitRouteEntryServer = () => (
type ProviderProps = {
customAcl?: Acl;
entity?: string;
resources?: ClusterResource[];
resources?: Resource[];
children?: React.ReactNode;
};
@@ -18,12 +18,13 @@
import { Platform, UserAgent } from 'design/platform';
import {
OnboardUserPreferences,
Resource,
} from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import { makeDefaultUserPreferences } from 'teleport/services/userPreferences/userPreferences';
import {
ClusterResource,
OnboardUserPreferences,
} from 'teleport/services/userPreferences/types';
import { OnboardDiscover } from 'teleport/services/user';
import { ResourceKind } from '../Shared';
@@ -228,12 +229,12 @@ describe('preferred resources', () => {
const testCases: {
name: string;
preferred: ClusterResource[];
preferred: Resource[];
expected: ResourceSpec[];
}[] = [
{
name: 'preferred server/ssh',
preferred: [ClusterResource.RESOURCE_SERVER_SSH],
preferred: [Resource.SERVER_SSH],
expected: [
// preferred first
f_Server,
@@ -255,7 +256,7 @@ describe('preferred resources', () => {
},
{
name: 'preferred databases',
preferred: [ClusterResource.RESOURCE_DATABASES],
preferred: [Resource.DATABASES],
expected: [
// preferred first
a_Database,
@@ -277,7 +278,7 @@ describe('preferred resources', () => {
},
{
name: 'preferred windows',
preferred: [ClusterResource.RESOURCE_WINDOWS_DESKTOPS],
preferred: [Resource.WINDOWS_DESKTOPS],
expected: [
// preferred first
i_Desktop,
@@ -299,7 +300,7 @@ describe('preferred resources', () => {
},
{
name: 'preferred applications',
preferred: [ClusterResource.RESOURCE_WEB_APPLICATIONS],
preferred: [Resource.WEB_APPLICATIONS],
expected: [
// preferred first
c_Application,
@@ -321,7 +322,7 @@ describe('preferred resources', () => {
},
{
name: 'preferred kubernetes',
preferred: [ClusterResource.RESOURCE_KUBERNETES],
preferred: [Resource.KUBERNETES],
expected: [
// preferred first; guided before unguided
j_Kubernetes,
@@ -369,7 +370,7 @@ describe('marketing params', () => {
{
name: 'marketing params instead of preferred resources',
preferred: {
preferredResources: [ClusterResource.RESOURCE_WEB_APPLICATIONS],
preferredResources: [Resource.WEB_APPLICATIONS],
marketingParams: {
campaign: 'kubernetes',
source: '',
@@ -24,6 +24,10 @@ import styled from 'styled-components';
import { Box, Flex, Link, Text } from 'design';
import { getPlatform, Platform } from 'design/platform';
import { UserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/userpreferences_pb';
import { Resource } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import useTeleport from 'teleport/useTeleport';
import { ToolTipNoPermBadge } from 'teleport/components/ToolTipNoPermBadge';
import { Acl, AuthType, OnboardDiscover } from 'teleport/services/user';
@@ -41,11 +45,6 @@ import AddApp from 'teleport/Apps/AddApp';
import { useUser } from 'teleport/User/UserContext';
import { storageService } from 'teleport/services/storageService';
import {
ClusterResource,
UserPreferences,
} from 'teleport/services/userPreferences/types';
import { resourceKindToPreferredResource } from 'teleport/Discover/Shared/ResourceKind';
import { getMarketingTermMatches } from './getMarketingTermMatches';
@@ -544,7 +543,7 @@ function getPrioritizedResources(
const preferredResources = preferences.onboard.preferredResources || [];
// hasPreferredResources will be false if all resources are selected
const maxResources = Object.keys(ClusterResource).length / 2 - 1;
const maxResources = Object.keys(Resource).length / 2 - 1;
const selectedAll = preferredResources.length === maxResources;
return {
@@ -16,14 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { ClusterResource } from 'teleport/services/userPreferences/types';
import { Resource } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import { SearchResource } from 'teleport/Discover/SelectResource/types';
export const resourceMapping: { [key in ClusterResource]: SearchResource } = {
[ClusterResource.RESOURCE_UNSPECIFIED]: SearchResource.UNSPECIFIED,
[ClusterResource.RESOURCE_DATABASES]: SearchResource.DATABASE,
[ClusterResource.RESOURCE_KUBERNETES]: SearchResource.KUBERNETES,
[ClusterResource.RESOURCE_SERVER_SSH]: SearchResource.SERVER,
[ClusterResource.RESOURCE_WEB_APPLICATIONS]: SearchResource.APPLICATION,
[ClusterResource.RESOURCE_WINDOWS_DESKTOPS]: SearchResource.DESKTOP,
export const resourceMapping: { [key in Resource]: SearchResource } = {
[Resource.UNSPECIFIED]: SearchResource.UNSPECIFIED,
[Resource.DATABASES]: SearchResource.DATABASE,
[Resource.KUBERNETES]: SearchResource.KUBERNETES,
[Resource.SERVER_SSH]: SearchResource.SERVER,
[Resource.WEB_APPLICATIONS]: SearchResource.APPLICATION,
[Resource.WINDOWS_DESKTOPS]: SearchResource.DESKTOP,
};
@@ -16,10 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import {
ClusterResource,
MarketingParams,
} from 'teleport/services/userPreferences/types';
import { Resource } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import { MarketingParams } from 'teleport/services/userPreferences/types';
import { getMarketingTermMatches } from './getMarketingTermMatches';
@@ -27,116 +26,107 @@ describe('getMarketingMatches', () => {
const testCases: {
name: string;
param: MarketingParams;
expected: ClusterResource[];
expected: Resource[];
}[] = [
{
name: 'database matches RESOURCE_DATABASES & k8s matches RESOURCE_KUBERNETES',
name: 'database matches DATABASES & k8s matches KUBERNETES',
param: {
campaign: 'foodatabasebar',
source: 'k8ski',
medium: '',
intent: '',
},
expected: [
ClusterResource.RESOURCE_DATABASES,
ClusterResource.RESOURCE_KUBERNETES,
],
expected: [Resource.DATABASES, Resource.KUBERNETES],
},
{
name: 'app matches RESOURCE_WEB_APPLICATIONS',
name: 'app matches WEB_APPLICATIONS',
param: {
campaign: '',
source: 'baz',
medium: '',
intent: 'fooappbar',
},
expected: [ClusterResource.RESOURCE_WEB_APPLICATIONS],
expected: [Resource.WEB_APPLICATIONS],
},
{
name: 'windows matches RESOURCE_WINDOWS_DESKTOPS',
name: 'windows matches WINDOWS_DESKTOPS',
param: {
campaign: 'foowindowsbar',
source: '',
medium: '',
intent: 'aws',
},
expected: [ClusterResource.RESOURCE_WINDOWS_DESKTOPS],
expected: [Resource.WINDOWS_DESKTOPS],
},
{
name: 'desktop matches RESOURCE_WINDOWS_DESKTOPS',
name: 'desktop matches WINDOWS_DESKTOPS',
param: {
campaign: '',
source: '',
medium: 'foodesktopbar',
intent: 'shoo',
},
expected: [ClusterResource.RESOURCE_WINDOWS_DESKTOPS],
expected: [Resource.WINDOWS_DESKTOPS],
},
{
name: 'ssh matches RESOURCE_SERVER_SSH',
name: 'ssh matches SERVER_SSH',
param: {
campaign: '',
source: 'foosshbar',
medium: 'bar',
intent: '',
},
expected: [ClusterResource.RESOURCE_SERVER_SSH],
expected: [Resource.SERVER_SSH],
},
{
name: 'server matches RESOURCE_SERVER_SSH',
name: 'server matches SERVER_SSH',
param: {
campaign: 'fooserverbar',
source: '',
medium: '',
intent: 'ser',
},
expected: [ClusterResource.RESOURCE_SERVER_SSH],
expected: [Resource.SERVER_SSH],
},
{
name: 'kube matches RESOURCE_KUBERNETES and windows matches RESOURCE_WINDOWS_DESKTOPS',
name: 'kube matches KUBERNETES and windows matches WINDOWS_DESKTOPS',
param: {
campaign: 'fookubebar',
source: '',
medium: 'windows',
intent: '',
},
expected: [
ClusterResource.RESOURCE_KUBERNETES,
ClusterResource.RESOURCE_WINDOWS_DESKTOPS,
],
expected: [Resource.KUBERNETES, Resource.WINDOWS_DESKTOPS],
},
{
name: 'kubernetes matches RESOURCE_KUBERNETES',
name: 'kubernetes matches KUBERNETES',
param: {
campaign: 'kubernetes',
source: '',
medium: '',
intent: '',
},
expected: [ClusterResource.RESOURCE_KUBERNETES],
expected: [Resource.KUBERNETES],
},
{
name: 'kube matches RESOURCE_KUBERNETES',
name: 'kube matches KUBERNETES',
param: {
campaign: '',
source: 'kube',
medium: '',
intent: '',
},
expected: [ClusterResource.RESOURCE_KUBERNETES],
expected: [Resource.KUBERNETES],
},
{
name: 'k8s matches RESOURCE_KUBERNETES and ssh matches RESOURCE_SERVER_SSH',
name: 'k8s matches KUBERNETES and ssh matches SERVER_SSH',
param: {
campaign: '',
source: '',
medium: 'fook8sbar',
intent: 'ssh',
},
expected: [
ClusterResource.RESOURCE_KUBERNETES,
ClusterResource.RESOURCE_SERVER_SSH,
],
expected: [Resource.KUBERNETES, Resource.SERVER_SSH],
},
{
name: 'aws does not match',
@@ -16,21 +16,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import {
ClusterResource,
MarketingParams,
} from 'teleport/services/userPreferences/types';
import { Resource } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import { MarketingParams } from 'teleport/services/userPreferences/types';
/**
* Returns a list of resource kinds that match provided marketing parameters.
*
* @param marketingParams - MarketingParams from cluster user preferences which are set at signup
* @returns an array of ClusterResource associated with the marketing params for resource discoverability
* @returns an array of Resource associated with the marketing params for resource discoverability
*
*/
export const getMarketingTermMatches = (
marketingParams: MarketingParams
): ClusterResource[] => {
): Resource[] => {
const params = [];
if (marketingParams) {
marketingParams.campaign && params.push(marketingParams.campaign);
@@ -42,7 +41,7 @@ export const getMarketingTermMatches = (
return [];
}
const matches = new Set<ClusterResource>();
const matches = new Set<Resource>();
params.forEach(p => {
Object.values(TermMatch).forEach(m => {
const clusterResource = matchTerm(m);
@@ -68,22 +67,22 @@ export enum TermMatch {
AWS = 'aws',
}
const matchTerm = (m: string): ClusterResource => {
const matchTerm = (m: string): Resource => {
switch (m) {
case TermMatch.App:
return ClusterResource.RESOURCE_WEB_APPLICATIONS;
return Resource.WEB_APPLICATIONS;
case TermMatch.Database:
return ClusterResource.RESOURCE_DATABASES;
return Resource.DATABASES;
case TermMatch.Kube:
case TermMatch.Kubernetes:
case TermMatch.K8s:
return ClusterResource.RESOURCE_KUBERNETES;
return Resource.KUBERNETES;
case TermMatch.SSH:
case TermMatch.Server:
return ClusterResource.RESOURCE_SERVER_SSH;
return Resource.SERVER_SSH;
case TermMatch.Desktop:
case TermMatch.Windows:
return ClusterResource.RESOURCE_WINDOWS_DESKTOPS;
return Resource.WINDOWS_DESKTOPS;
// currently we have no resource kind nor cluster resource defined for AWS
// in the future, we can search the resources based on this term.
case TermMatch.AWS:
@@ -18,7 +18,8 @@
import { Platform } from 'design/platform';
import { ClusterResource } from 'teleport/services/userPreferences/types';
import { Resource } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import { AuthType } from 'teleport/services/user';
import { ResourceKind } from '../Shared/ResourceKind';
@@ -132,6 +133,6 @@ export enum SearchResource {
}
export type PrioritizedResources = {
preferredResources: ClusterResource[];
preferredResources: Resource[];
hasPreferredResources: boolean;
};
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { ClusterResource } from 'teleport/services/userPreferences/types';
import { Resource } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import type { JoinRole } from 'teleport/services/joinToken';
@@ -50,23 +50,21 @@ export function resourceKindToJoinRole(kind: ResourceKind): JoinRole {
}
}
export function resourceKindToPreferredResource(
kind: ResourceKind
): ClusterResource {
export function resourceKindToPreferredResource(kind: ResourceKind): Resource {
switch (kind) {
case ResourceKind.Application:
return ClusterResource.RESOURCE_WEB_APPLICATIONS;
return Resource.WEB_APPLICATIONS;
case ResourceKind.Database:
return ClusterResource.RESOURCE_DATABASES;
return Resource.DATABASES;
case ResourceKind.Desktop:
return ClusterResource.RESOURCE_WINDOWS_DESKTOPS;
return Resource.WINDOWS_DESKTOPS;
case ResourceKind.Kubernetes:
return ClusterResource.RESOURCE_KUBERNETES;
return Resource.KUBERNETES;
case ResourceKind.Server:
return ClusterResource.RESOURCE_SERVER_SSH;
return Resource.SERVER_SSH;
case ResourceKind.Discovery:
return ClusterResource.RESOURCE_UNSPECIFIED;
return Resource.UNSPECIFIED;
case ResourceKind.ConnectMyComputer:
return ClusterResource.RESOURCE_SERVER_SSH;
return Resource.SERVER_SSH;
}
}
+3 -2
View File
@@ -37,6 +37,8 @@ import { matchPath, useHistory } from 'react-router';
import Dialog from 'design/Dialog';
import { sharedStyles } from 'design/theme/themes/sharedStyles';
import { AssistViewMode } from 'gen-proto-ts/teleport/userpreferences/v1/assist_pb';
import { Redirect, Route, Switch } from 'teleport/components/Router';
import { CatchError } from 'teleport/components/CatchError';
import cfg from 'teleport/config';
@@ -54,7 +56,6 @@ import {
import { NavigationCategory } from 'teleport/Navigation/categories';
import { TopBarProps } from 'teleport/TopBar/TopBar';
import { useUser } from 'teleport/User/UserContext';
import { ViewMode } from 'teleport/Assist/types';
import { QuestionnaireProps } from 'teleport/Welcome/NewCredentials';
import { MainContainer } from './MainContainer';
@@ -196,7 +197,7 @@ export function Main(props: MainProps) {
<MainContainer>
<Navigation />
<HorizontalSplit
dockedView={showAssist && viewMode === ViewMode.Docked}
dockedView={showAssist && viewMode === AssistViewMode.DOCKED}
hasSidebar={feature?.category === NavigationCategory.Management}
>
<ContentMinWidth>
@@ -27,9 +27,10 @@ import {
useUnifiedResourcesFetch,
UnifiedResourcesPinning,
} from 'shared/components/UnifiedResources';
import { DefaultTab } from 'shared/services/unifiedResourcePreferences';
import { ClusterDropdown } from 'shared/components/ClusterDropdown/ClusterDropdown';
import { DefaultTab } from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb';
import useStickyClusterId from 'teleport/useStickyClusterId';
import { storageService } from 'teleport/services/storageService';
import { useUser } from 'teleport/User/UserContext';
@@ -123,8 +124,7 @@ function ClusterResources({
dir: 'ASC',
},
pinnedOnly:
preferences?.unifiedResourcePreferences?.defaultTab ===
DefaultTab.DEFAULT_TAB_PINNED,
preferences?.unifiedResourcePreferences?.defaultTab === DefaultTab.PINNED,
});
const getCurrentClusterPinnedResources = useCallback(
@@ -26,11 +26,12 @@ import { render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import { Theme } from 'gen-proto-ts/teleport/userpreferences/v1/theme_pb';
import cfg from 'teleport/config';
import { UserContextProvider } from 'teleport/User';
import { ThemePreference } from 'teleport/services/userPreferences/types';
import { useUser } from 'teleport/User/UserContext';
import { KeysEnum } from 'teleport/services/storageService';
@@ -38,9 +39,7 @@ function ThemeName() {
const { preferences } = useUser();
return (
<div>
theme: {preferences.theme === ThemePreference.Light ? 'light' : 'dark'}
</div>
<div>theme: {preferences.theme === Theme.LIGHT ? 'light' : 'dark'}</div>
);
}
@@ -49,7 +48,7 @@ describe('user context - success state', () => {
rest.get(cfg.api.userPreferencesPath, (req, res, ctx) => {
return res(
ctx.json({
theme: ThemePreference.Light,
theme: Theme.LIGHT,
assist: {},
})
);
@@ -76,7 +75,7 @@ describe('user context - success state', () => {
});
it('should migrate the previous theme setting from local storage', async () => {
let updateBody: { theme?: ThemePreference } = {};
let updateBody: { theme?: Theme } = {};
server.use(
rest.put(cfg.api.userPreferencesPath, async (req, res, ctx) => {
@@ -96,7 +95,7 @@ describe('user context - success state', () => {
</MemoryRouter>
);
await waitFor(() => expect(updateBody.theme).toEqual(ThemePreference.Dark));
await waitFor(() => expect(updateBody.theme).toEqual(Theme.DARK));
const theme = await screen.findByText(/theme: dark/i);
+16 -15
View File
@@ -30,6 +30,12 @@ import useAttempt from 'shared/hooks/useAttemptNext';
import { Indicator } from 'design';
import { UserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/userpreferences_pb';
import { ClusterUserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/cluster_preferences_pb';
import { Theme } from 'gen-proto-ts/teleport/userpreferences/v1/theme_pb';
import { StyledIndicator } from 'teleport/Main';
import * as service from 'teleport/services/userPreferences';
@@ -37,18 +43,10 @@ import cfg from 'teleport/config';
import { KeysEnum, storageService } from 'teleport/services/storageService';
import {
deprecatedThemeToThemePreference,
ThemePreference,
} from 'teleport/services/userPreferences/types';
import { deprecatedThemeToThemePreference } from 'teleport/services/userPreferences/types';
import { makeDefaultUserPreferences } from 'teleport/services/userPreferences/userPreferences';
import type {
UserClusterPreferences,
UserPreferences,
} from 'teleport/services/userPreferences/types';
export interface UserContextValue {
preferences: UserPreferences;
updatePreferences: (preferences: Partial<UserPreferences>) => Promise<void>;
@@ -69,7 +67,7 @@ export function UserContextProvider(props: PropsWithChildren<unknown>) {
const { attempt, run } = useAttempt('processing');
// because we have to update cluster preferences with itself during the update
// we useRef here to prevent infinite rerenders
const clusterPreferences = useRef<Record<string, UserClusterPreferences>>({});
const clusterPreferences = useRef<Record<string, ClusterUserPreferences>>({});
const [preferences, setPreferences] = useState<UserPreferences>(
makeDefaultUserPreferences()
@@ -80,12 +78,12 @@ export function UserContextProvider(props: PropsWithChildren<unknown>) {
// we know that pinned resources is supported because we've already successfully
// fetched their pinned resources once before
window.localStorage.removeItem(KeysEnum.PINNED_RESOURCES_NOT_SUPPORTED);
return clusterPreferences.current[clusterId].pinnedResources;
return clusterPreferences.current[clusterId].pinnedResources.resourceIds;
}
const prefs = await service.getUserClusterPreferences(clusterId);
if (prefs) {
clusterPreferences.current[clusterId] = prefs;
return prefs.pinnedResources;
return prefs.pinnedResources.resourceIds;
}
return null;
}, []);
@@ -95,9 +93,12 @@ export function UserContextProvider(props: PropsWithChildren<unknown>) {
pinnedResources: string[]
) => {
if (!clusterPreferences.current[clusterId]) {
clusterPreferences.current[clusterId] = { pinnedResources: [] };
clusterPreferences.current[clusterId] = {
pinnedResources: { resourceIds: [] },
};
}
clusterPreferences.current[clusterId].pinnedResources = pinnedResources;
clusterPreferences.current[clusterId].pinnedResources.resourceIds =
pinnedResources;
return service.updateUserClusterPreferences(clusterId, {
...preferences,
@@ -121,7 +122,7 @@ export function UserContextProvider(props: PropsWithChildren<unknown>) {
if (theme) {
preferences.theme = deprecatedThemeToThemePreference(theme);
if (preferences.theme !== ThemePreference.Light) {
if (preferences.theme !== Theme.LIGHT) {
// the light theme is the default, so only update the backend if it is not light
updatePreferences(preferences);
}
@@ -28,6 +28,8 @@ import {
fireEvent,
} from 'design/utils/testing';
import { Theme } from 'gen-proto-ts/teleport/userpreferences/v1/theme_pb';
import cfg from 'teleport/config';
import { FeaturesContextProvider } from 'teleport/FeaturesContext';
@@ -38,8 +40,6 @@ import TeleportContext from 'teleport/teleportContext';
import { makeUserContext } from 'teleport/services/user';
import { ThemePreference } from 'teleport/services/userPreferences/types';
import { mockUserContextProviderWith } from 'teleport/User/testHelpers/mockUserContextWith';
import { makeTestUserContext } from 'teleport/User/testHelpers/makeTestUserContext';
@@ -49,7 +49,7 @@ const server = setupServer(
rest.get(cfg.api.userPreferencesPath, (req, res, ctx) => {
return res(
ctx.json({
theme: ThemePreference.Light,
theme: Theme.LIGHT,
assist: {},
})
);
@@ -23,11 +23,12 @@ import { Moon, Sun, ChevronDown, Logout as LogoutIcon } from 'design/Icon';
import { Text } from 'design';
import { useRefClickOutside } from 'shared/hooks/useRefClickOutside';
import { Theme } from 'gen-proto-ts/teleport/userpreferences/v1/theme_pb';
import session from 'teleport/services/websession';
import { useFeatures } from 'teleport/FeaturesContext';
import { useTeleport } from 'teleport';
import { useUser } from 'teleport/User/UserContext';
import { ThemePreference } from 'teleport/services/userPreferences/types';
import {
Dropdown,
DropdownItem,
@@ -124,9 +125,7 @@ export function UserMenuNav({ username }: UserMenuNavProps) {
const onThemeChange = () => {
const nextTheme =
preferences.theme === ThemePreference.Light
? ThemePreference.Dark
: ThemePreference.Light;
preferences.theme === Theme.LIGHT ? Theme.DARK : Theme.LIGHT;
updatePreferences({ theme: nextTheme });
setOpen(false);
@@ -180,14 +179,9 @@ export function UserMenuNav({ username }: UserMenuNavProps) {
<DropdownItem open={open} $transitionDelay={transitionDelay}>
<DropdownItemButton onClick={onThemeChange}>
<DropdownItemIcon>
{preferences.theme === ThemePreference.Dark ? (
<Sun />
) : (
<Moon />
)}
{preferences.theme === Theme.DARK ? <Sun /> : <Moon />}
</DropdownItemIcon>
Switch to{' '}
{preferences.theme === ThemePreference.Dark ? 'Light' : 'Dark'}{' '}
Switch to {preferences.theme === Theme.DARK ? 'Light' : 'Dark'}{' '}
Theme
</DropdownItemButton>
</DropdownItem>
@@ -18,14 +18,20 @@
import { DeprecatedThemeOption } from 'design/theme/types';
import { UserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/userpreferences_pb';
import { Theme } from 'gen-proto-ts/teleport/userpreferences/v1/theme_pb';
import { OnboardUserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import { BearerToken } from 'teleport/services/websession';
import { OnboardDiscover } from 'teleport/services/user';
import {
OnboardUserPreferences,
ThemePreference,
UserPreferences,
} from 'teleport/services/userPreferences/types';
BackendUserPreferences,
convertBackendUserPreferences,
isBackendUserPreferences,
} from 'teleport/services/userPreferences/userPreferences';
import { CloudUserInvites, KeysEnum, LocalStorageSurvey } from './types';
@@ -107,7 +113,17 @@ export const storageService = {
getUserPreferences(): UserPreferences {
const preferences = window.localStorage.getItem(KeysEnum.USER_PREFERENCES);
if (preferences) {
return JSON.parse(preferences);
const parsed: UserPreferences | BackendUserPreferences =
JSON.parse(preferences);
// TODO(ryan): DELETE in v17: remove reference to `BackendUserPreferences` - all
// locally stored copies of user preferences should be `UserPreferences` by then
// (they are updated on every login)
if (isBackendUserPreferences(parsed)) {
return convertBackendUserPreferences(parsed);
}
return parsed;
}
return null;
},
@@ -161,7 +177,7 @@ export const storageService = {
window.localStorage.removeItem(KeysEnum.CLOUD_USER_INVITES);
},
getThemePreference(): ThemePreference {
getThemePreference(): Theme {
const userPreferences = storageService.getUserPreferences();
if (userPreferences) {
return userPreferences.theme;
@@ -169,10 +185,10 @@ export const storageService = {
const theme = this.getDeprecatedThemePreference();
if (theme) {
return theme === 'light' ? ThemePreference.Light : ThemePreference.Dark;
return theme === 'light' ? Theme.LIGHT : Theme.DARK;
}
return ThemePreference.Light;
return Theme.LIGHT;
},
getOnboardUserPreference(): OnboardUserPreferences {
@@ -18,23 +18,7 @@
import { DeprecatedThemeOption } from 'design/theme';
import type { UnifiedResourcePreferences } from 'shared/services/unifiedResourcePreferences';
import type { AssistUserPreferences } from 'teleport/Assist/types';
export enum ThemePreference {
Light = 1,
Dark = 2,
}
export enum ClusterResource {
RESOURCE_UNSPECIFIED = 0,
RESOURCE_WINDOWS_DESKTOPS = 1,
RESOURCE_SERVER_SSH = 2,
RESOURCE_DATABASES = 3,
RESOURCE_KUBERNETES = 4,
RESOURCE_WEB_APPLICATIONS = 5,
}
import { Theme } from 'gen-proto-ts/teleport/userpreferences/v1/theme_pb';
export type MarketingParams = {
campaign: string;
@@ -43,36 +27,13 @@ export type MarketingParams = {
intent: string;
};
export type OnboardUserPreferences = {
preferredResources: ClusterResource[];
marketingParams: MarketingParams;
};
export interface UserPreferences {
theme: ThemePreference;
assist: AssistUserPreferences;
onboard: OnboardUserPreferences;
clusterPreferences: UserClusterPreferences;
unifiedResourcePreferences: UnifiedResourcePreferences;
}
// UserClusterPreferences are user preferences that are
// different per cluster.
export interface UserClusterPreferences {
// pinnedResources is an array of resource IDs.
pinnedResources: string[];
}
export type GetUserClusterPreferencesResponse = UserClusterPreferences;
export type GetUserPreferencesResponse = UserPreferences;
export function deprecatedThemeToThemePreference(
theme: DeprecatedThemeOption
): ThemePreference {
): Theme {
switch (theme) {
case 'light':
return ThemePreference.Light;
return Theme.LIGHT;
case 'dark':
return ThemePreference.Dark;
return Theme.DARK;
}
}
@@ -0,0 +1,74 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Theme } from 'gen-proto-ts/teleport/userpreferences/v1/theme_pb';
import { UserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/userpreferences_pb';
import {
BackendUserPreferences,
convertBackendUserPreferences,
convertUserPreferences,
isBackendUserPreferences,
} from 'teleport/services/userPreferences/userPreferences';
test('should convert the old cluster user preferences format to the new one', () => {
// this is how the backend currently returns cluster preferences - as an array of strings
// instead of the protobuf representation of an object with a `resourceIds` field that contains
// that array of strings
const oldBackendPreferences: BackendUserPreferences = {
theme: Theme.LIGHT,
clusterPreferences: {
pinnedResources: ['resource1', 'resource2'],
},
};
const actualUserPreferences: UserPreferences = {
theme: Theme.LIGHT,
clusterPreferences: {
pinnedResources: { resourceIds: ['resource1', 'resource2'] },
},
};
// when we grab the user preferences from the local storage, we check if it is in the old format
expect(isBackendUserPreferences(oldBackendPreferences)).toBe(true);
expect(isBackendUserPreferences(actualUserPreferences)).toBe(false);
// and convert it to the new format if it is
const newPreferences = convertBackendUserPreferences(oldBackendPreferences);
expect(newPreferences.clusterPreferences.pinnedResources.resourceIds).toEqual(
oldBackendPreferences.clusterPreferences.pinnedResources
);
});
test('should convert the user preferences back to the old format when updating', () => {
// the backend still expects the old format when updating user preferences
const actualUserPreferences: UserPreferences = {
theme: Theme.LIGHT,
clusterPreferences: {
pinnedResources: { resourceIds: ['resource1', 'resource2'] },
},
};
const convertedPreferences = convertUserPreferences(actualUserPreferences);
expect(convertedPreferences.clusterPreferences.pinnedResources).toEqual(
actualUserPreferences.clusterPreferences.pinnedResources.resourceIds
);
});
@@ -16,40 +16,63 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import * as unifiedResourcePreferences from 'shared/services/unifiedResourcePreferences';
import {
DefaultTab,
LabelsViewMode,
UnifiedResourcePreferences,
ViewMode,
} from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb';
import {
AssistUserPreferences,
AssistViewMode,
} from 'gen-proto-ts/teleport/userpreferences/v1/assist_pb';
import { UserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/userpreferences_pb';
import { ClusterUserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/cluster_preferences_pb';
import { Theme } from 'gen-proto-ts/teleport/userpreferences/v1/theme_pb';
import { OnboardUserPreferences } from 'gen-proto-ts/teleport/userpreferences/v1/onboard_pb';
import cfg from 'teleport/config';
import api from 'teleport/services/api';
import { ViewMode } from 'teleport/Assist/types';
import { KeysEnum } from '../storageService';
import { ThemePreference } from './types';
interface BackendClusterUserPreferences {
pinnedResources?: string[];
}
import type {
GetUserPreferencesResponse,
UserClusterPreferences,
UserPreferences,
} from 'teleport/services/userPreferences/types';
export interface BackendUserPreferences {
assist?: AssistUserPreferences;
theme: Theme;
onboard?: OnboardUserPreferences;
clusterPreferences?: BackendClusterUserPreferences;
unifiedResourcePreferences?: UnifiedResourcePreferences;
}
export async function getUserPreferences() {
const res: GetUserPreferencesResponse = await api.get(
export async function getUserPreferences(): Promise<UserPreferences> {
const res: BackendUserPreferences = await api.get(
cfg.api.userPreferencesPath
);
return res;
return convertBackendUserPreferences(res);
}
export async function getUserClusterPreferences(clusterId: string) {
export async function getUserClusterPreferences(
clusterId: string
): Promise<ClusterUserPreferences> {
return await api
.get(cfg.getUserClusterPreferencesUrl(clusterId))
.then(res => {
.then((res: BackendClusterUserPreferences) => {
// TODO (avatus) DELETE IN 16
// this item is used to disabled the pinned resources button if they
// haven't upgraded to 14.1.0 yet. Anything lower than 14 doesn't matter
// because the unified resource view isn't enabled so pinning isn't there either
localStorage.removeItem(KeysEnum.PINNED_RESOURCES_NOT_SUPPORTED);
return res;
return convertBackendClusterUserPreferences(res);
})
.catch(res => {
if (res.response?.status === 403 || res.response?.status === 404) {
@@ -68,18 +91,24 @@ export function updateUserClusterPreferences(
clusterId: string,
preferences: UserPreferences
) {
return api.put(cfg.getUserClusterPreferencesUrl(clusterId), preferences);
return api.put(
cfg.getUserClusterPreferencesUrl(clusterId),
convertUserPreferences(preferences)
);
}
export function updateUserPreferences(preferences: Partial<UserPreferences>) {
return api.put(cfg.api.userPreferencesPath, preferences);
return api.put(
cfg.api.userPreferencesPath,
convertUserPreferences(preferences as UserPreferences)
);
}
export function makeDefaultUserPreferences(): UserPreferences {
return {
theme: ThemePreference.Light,
theme: Theme.LIGHT,
assist: {
viewMode: ViewMode.Docked,
viewMode: AssistViewMode.DOCKED,
preferredLogins: [],
},
onboard: {
@@ -92,17 +121,61 @@ export function makeDefaultUserPreferences(): UserPreferences {
},
},
unifiedResourcePreferences: {
defaultTab: unifiedResourcePreferences.DefaultTab.DEFAULT_TAB_ALL,
viewMode: unifiedResourcePreferences.ViewMode.VIEW_MODE_CARD,
labelsViewMode:
unifiedResourcePreferences.LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED,
defaultTab: DefaultTab.ALL,
viewMode: ViewMode.CARD,
labelsViewMode: LabelsViewMode.COLLAPSED,
},
clusterPreferences: makeDefaultUserClusterPreferences(),
};
}
export function makeDefaultUserClusterPreferences(): UserClusterPreferences {
export function makeDefaultUserClusterPreferences(): ClusterUserPreferences {
return {
pinnedResources: [],
pinnedResources: {
resourceIds: [],
},
};
}
export function convertUserPreferences(
preferences: UserPreferences
): BackendUserPreferences {
return {
...preferences,
clusterPreferences: {
...preferences.clusterPreferences,
pinnedResources:
preferences.clusterPreferences?.pinnedResources?.resourceIds ?? [],
},
};
}
export function convertBackendUserPreferences(
preferences: BackendUserPreferences
): UserPreferences {
return {
...preferences,
clusterPreferences: convertBackendClusterUserPreferences(
preferences.clusterPreferences
),
};
}
export function convertBackendClusterUserPreferences(
clusterPreferences: BackendClusterUserPreferences
): ClusterUserPreferences {
return {
...clusterPreferences,
pinnedResources: {
resourceIds: clusterPreferences?.pinnedResources ?? [],
},
};
}
export function isBackendUserPreferences(
preferences: UserPreferences | BackendUserPreferences
): preferences is BackendUserPreferences {
return Array.isArray(
(preferences as BackendUserPreferences).clusterPreferences?.pinnedResources
);
}
-7
View File
@@ -1,11 +1,4 @@
const baseCfg = require('@gravitational/build/.babelrc');
baseCfg.presets = [
['@babel/preset-env', { targets: { node: 'current' } }],
['@babel/preset-react', { runtime: 'automatic' }],
'@babel/preset-typescript',
];
module.exports = function (api) {
api.cache(true);
return baseCfg;
+42
View File
@@ -0,0 +1,42 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export function getConnectCsp(development: boolean) {
// feedbackAddress needs to be kept in sync with the same property in staticConfig.ts.
const feedbackAddress = development
? 'https://kcwm2is93l.execute-api.us-west-2.amazonaws.com/prod'
: 'https://usage.teleport.dev';
let csp = `
default-src 'self';
connect-src 'self' ${feedbackAddress};
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
object-src 'none';
font-src 'self' data:;
`
.replaceAll('\n', ' ')
.trim();
if (development) {
// Required to make source maps work in dev mode.
csp += " script-src 'self' 'unsafe-eval' 'unsafe-inline';";
}
return csp;
}
@@ -71,7 +71,7 @@ module.exports = {
}
},
files: [
'build/app/dist',
'build/app',
// node-pty creates some files that differ across architecture builds causing
// the error "can't reconcile the non-macho files" as they cant be combined
// with lipo for a universal build. They aren't needed so skip them.
@@ -0,0 +1,170 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { resolve } from 'path';
import { existsSync, readFileSync } from 'fs';
import react from '@vitejs/plugin-react-swc';
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig, externalizeDepsPlugin, UserConfig } from 'electron-vite';
import { cspPlugin } from '../build/vite/csp';
import { getStyledComponentsConfig } from '../build/vite/styled';
import { getConnectCsp } from './csp';
const rootDirectory = resolve(__dirname, '../../..');
const outputDirectory = resolve(__dirname, 'build', 'app');
// these dependencies don't play well unless they're externalized
// if Vite complains about a dependency, add it here
const externalizeDeps = ['strip-ansi', 'ansi-regex', 'd3-color'];
const config = defineConfig(env => {
const tsConfigPathsPlugin = tsconfigPaths({
projects: [resolve(rootDirectory, 'tsconfig.json')],
});
const commonPlugins = [
externalizeDepsPlugin({ exclude: externalizeDeps }),
tsConfigPathsPlugin,
];
const config: UserConfig = {
main: {
build: {
outDir: resolve(outputDirectory, 'main'),
rollupOptions: {
input: {
index: resolve(__dirname, 'src/main.ts'),
sharedProcess: resolve(
__dirname,
'src/sharedProcess/sharedProcess.ts'
),
agentCleanupDaemon: resolve(
__dirname,
'src/agentCleanupDaemon/agentCleanupDaemon.js'
),
},
output: {
manualChunks,
},
},
},
plugins: commonPlugins,
define: {
// It's not common to pre-process Node code with NODE_ENV, but this is what our Webpack
// config used to do, so for compatibility purposes we kept the Vite config this way.
//
// If we were to get rid of this, we'd somehow need to set NODE_ENV when the packaged app
// gets started.
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
},
},
preload: {
build: {
outDir: resolve(outputDirectory, 'preload'),
rollupOptions: {
input: {
index: resolve(__dirname, 'src/preload.ts'),
},
output: {
manualChunks,
},
},
},
plugins: commonPlugins,
define: {
// Preload is also mean to be run by Node, see the comment for define under main.
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
},
},
renderer: {
root: '.',
build: {
outDir: resolve(outputDirectory, 'renderer'),
rollupOptions: {
input: {
index: resolve(__dirname, 'index.html'),
},
},
},
server: {
host: '0.0.0.0',
port: 8080,
fs: {
allow: [rootDirectory, '.'],
},
},
plugins: [
react({
plugins: [
[
'@swc/plugin-styled-components',
getStyledComponentsConfig(env.mode),
],
],
}),
cspPlugin(getConnectCsp(env.mode === 'development')),
tsConfigPathsPlugin,
],
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
},
},
};
if (env.mode === 'development') {
if (process.env.VITE_HTTPS_KEY && process.env.VITE_HTTPS_CERT) {
config.renderer.server.https = {
key: readFileSync(process.env.VITE_HTTPS_KEY),
cert: readFileSync(process.env.VITE_HTTPS_CERT),
};
} else {
const certsDirectory = resolve(rootDirectory, 'web/certs');
if (!existsSync(certsDirectory)) {
throw new Error(
'Could not find SSL certificates. Please follow web/README.md to generate certificates.'
);
}
const keyPath = resolve(certsDirectory, 'server.key');
const certPath = resolve(certsDirectory, 'server.crt');
config.renderer.server.https = {
key: readFileSync(keyPath),
cert: readFileSync(certPath),
};
}
}
return config;
});
export { config as default };
function manualChunks(id: string) {
for (const dep of externalizeDeps) {
if (id.includes(dep)) {
return dep;
}
}
}
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="referrer" content="no-referrer" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/ui/boot.tsx"></script>
</body>
</html>
+16 -15
View File
@@ -2,7 +2,7 @@
"name": "@gravitational/teleterm",
"version": "1.0.0-dev",
"description": "Teleport Connect",
"main": "build/app/dist/main/main.js",
"main": "build/app/main/index.js",
"author": {
"name": "Gravitational, Inc.",
"email": "[email protected]",
@@ -10,15 +10,13 @@
},
"license": "Apache-2.0",
"scripts": {
"start": "yarn build-native-deps && webpack serve --config webpack.renderer.dev.config.js --progress",
"start-main": "webpack build --config webpack.main.config.js --mode=development --progress --watch",
"start": "yarn build-native-deps && yarn start-skip-native-deps",
"start-skip-native-deps": "electron-vite dev",
"start-electron": "electron build/app/dist/main/main.js",
"build": "yarn build-main && yarn build-renderer",
"build-main": "webpack build --config webpack.main.config.js --progress --mode=production",
"build-renderer": "webpack build --config webpack.renderer.prod.config.js --progress",
"build": "electron-vite build",
"build-native-deps": "electron-builder install-app-deps",
"package": "electron-builder build --config electron-builder-config.js --publish never -c.extraMetadata.name=teleport-connect",
"generate-grpc-shared": "npx -y --target_arch=x64 --package=[email protected] [email protected] -- grpc_tools_node_protoc -I=src/sharedProcess/api/proto --ts_out=service=grpc-node,mode=grpc-js:src/sharedProcess/api/protogen --grpc_out=grpc_js:src/sharedProcess/api/protogen --js_out=import_style=commonjs,binary:src/sharedProcess/api/protogen src/sharedProcess/api/proto/*.proto"
"generate-grpc-shared": "npx -y --target_arch=x64 --package=@protobuf-ts/[email protected] -- protoc -I=src/sharedProcess/api/proto --ts_opt long_type_number,eslint_disable,add_pb_suffix,client_grpc1,server_grpc1,ts_nocheck --ts_out=src/sharedProcess/api/protogen src/sharedProcess/api/proto/*.proto"
},
"repository": {
"type": "git",
@@ -28,35 +26,38 @@
"private": true,
"homepage": "https://goteleport.com",
"dependencies": {
"@types/tar-fs": "^2.0.1",
"@grpc/grpc-js": "1.8.8",
"node-forge": "^1.3.1",
"node-pty": "1.1.0-beta5",
"split2": "4.1.0",
"strip-ansi": "^7.1.0",
"tar-fs": "^3.0.3"
"tar-fs": "^3.0.3",
"winston": "^3.3.3"
},
"devDependencies": {
"@gravitational/build": "^1.0.0",
"@gravitational/design": "1.0.0",
"@gravitational/shared": "1.0.0",
"@grpc/grpc-js": "1.8.8",
"@protobuf-ts/runtime": "^2.9.3",
"@protobuf-ts/runtime-rpc": "^2.9.3",
"@types/google-protobuf": "^3.10.0",
"@types/node-forge": "^1.0.4",
"@types/tar-fs": "^2.0.1",
"@types/whatwg-url": "^11.0.1",
"clean-webpack-plugin": "4.0.0",
"cross-env": "5.0.5",
"electron": "28.1.1",
"electron-notarize": "^1.2.1",
"electron-vite": "^2.0.0",
"eslint-import-resolver-webpack": "0.13.2",
"eslint-loader": "3.0.3",
"google-protobuf": "^3.20.1",
"google-protobuf": "^3.21.2",
"immer": "^9.0.7",
"jest-canvas-mock": "^2.3.1",
"node-forge": "^1.3.1",
"react-dnd": "^14.0.4",
"react-dnd-html5-backend": "^14.0.2",
"split2": "4.1.0",
"ts-loader": "^9.4.2",
"vite-plugin-commonjs": "^0.10.1",
"whatwg-url": "^13.0.0",
"winston": "^3.3.3",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"zod": "^3.22.3",
+198
View File
@@ -0,0 +1,198 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// ExcludesFalse is a method that can be used instead of [].filter(Boolean)
// this removes the false values from the array type
import { PaginatedResource } from 'gen-proto-ts/teleport/lib/teleterm/v1/service_pb';
import { Server } from 'gen-proto-ts/teleport/lib/teleterm/v1/server_pb';
import { Database } from 'gen-proto-ts/teleport/lib/teleterm/v1/database_pb';
import { App } from 'gen-proto-ts/teleport/lib/teleterm/v1/app_pb';
import { Kube } from 'gen-proto-ts/teleport/lib/teleterm/v1/kube_pb';
import * as prehog from 'gen-proto-ts/prehog/v1alpha/connect_pb';
import {
PtyClientEvent,
PtyEventData,
PtyEventExit,
PtyEventResize,
PtyEventStart,
PtyEventStartError,
PtyServerEvent,
} from 'teleterm/sharedProcess/api/protogen/ptyHostService_pb';
export function resourceOneOfIsServer(
resource: PaginatedResource['resource']
): resource is {
oneofKind: 'server';
server: Server;
} {
return resource.oneofKind === 'server';
}
export function resourceOneOfIsDatabase(
resource: PaginatedResource['resource']
): resource is {
oneofKind: 'database';
database: Database;
} {
return resource.oneofKind === 'database';
}
export function resourceOneOfIsApp(
resource: PaginatedResource['resource']
): resource is {
oneofKind: 'app';
app: App;
} {
return resource.oneofKind === 'app';
}
export function resourceOneOfIsKube(
resource: PaginatedResource['resource']
): resource is {
oneofKind: 'kube';
kube: Kube;
} {
return resource.oneofKind === 'kube';
}
export function ptyEventOneOfIsStart(
event: PtyClientEvent['event'] | PtyServerEvent['event']
): event is {
oneofKind: 'start';
start: PtyEventStart;
} {
return event.oneofKind === 'start';
}
export function ptyEventOneOfIsData(
event: PtyClientEvent['event'] | PtyServerEvent['event']
): event is {
oneofKind: 'data';
data: PtyEventData;
} {
return event.oneofKind === 'data';
}
export function ptyEventOneOfIsResize(
event: PtyClientEvent['event'] | PtyServerEvent['event']
): event is {
oneofKind: 'resize';
resize: PtyEventResize;
} {
return event.oneofKind === 'resize';
}
export function ptyEventOneOfIsExit(
event: PtyClientEvent['event'] | PtyServerEvent['event']
): event is {
oneofKind: 'exit';
exit: PtyEventExit;
} {
return event.oneofKind === 'exit';
}
export function ptyEventOneOfIsStartError(
event: PtyClientEvent['event'] | PtyServerEvent['event']
): event is {
oneofKind: 'startError';
startError: PtyEventStartError;
} {
return event.oneofKind === 'startError';
}
export function connectEventOneOfIsClusterLogin(
event: prehog.SubmitConnectEventRequest['event']
): event is {
oneofKind: 'clusterLogin';
clusterLogin: prehog.ConnectClusterLoginEvent;
} {
return event.oneofKind === 'clusterLogin';
}
export function connectEventOneOfIsProtocolUse(
event: prehog.SubmitConnectEventRequest['event']
): event is {
oneofKind: 'protocolUse';
protocolUse: prehog.ConnectProtocolUseEvent;
} {
return event.oneofKind === 'protocolUse';
}
export function connectEventOneOfIsAccessRequestCreate(
event: prehog.SubmitConnectEventRequest['event']
): event is {
oneofKind: 'accessRequestCreate';
accessRequestCreate: prehog.ConnectAccessRequestCreateEvent;
} {
return event.oneofKind === 'accessRequestCreate';
}
export function connectEventOneOfIsAccessRequestReview(
event: prehog.SubmitConnectEventRequest['event']
): event is {
oneofKind: 'accessRequestReview';
accessRequestReview: prehog.ConnectAccessRequestReviewEvent;
} {
return event.oneofKind === 'accessRequestReview';
}
export function connectEventOneOfIsAccessRequestAssumeRole(
event: prehog.SubmitConnectEventRequest['event']
): event is {
oneofKind: 'accessRequestAssumeRole';
accessRequestAssumeRole: prehog.ConnectAccessRequestAssumeRoleEvent;
} {
return event.oneofKind === 'accessRequestAssumeRole';
}
export function connectEventOneOfIsFileTransferRun(
event: prehog.SubmitConnectEventRequest['event']
): event is {
oneofKind: 'fileTransferRun';
fileTransferRun: prehog.ConnectFileTransferRunEvent;
} {
return event.oneofKind === 'fileTransferRun';
}
export function connectEventOneOfIsUserJobRoleUpdate(
event: prehog.SubmitConnectEventRequest['event']
): event is {
oneofKind: 'userJobRoleUpdate';
userJobRoleUpdate: prehog.ConnectUserJobRoleUpdateEvent;
} {
return event.oneofKind === 'userJobRoleUpdate';
}
export function connectEventOneOfIsConnectMyComputerSetup(
event: prehog.SubmitConnectEventRequest['event']
): event is {
oneofKind: 'connectMyComputerSetup';
connectMyComputerSetup: prehog.ConnectConnectMyComputerSetup;
} {
return event.oneofKind === 'connectMyComputerSetup';
}
export function connectEventOneOfIsConnectMyComputerAgentStart(
event: prehog.SubmitConnectEventRequest['event']
): event is {
oneofKind: 'connectMyComputerAgentStart';
connectMyComputerAgentStart: prehog.ConnectConnectMyComputerAgentStart;
} {
return event.oneofKind === 'connectMyComputerAgentStart';
}
+5
View File
@@ -91,6 +91,7 @@ function initializeApp(): void {
windowsManager,
});
//TODO(gzdunek): Make sure this is not needed after migrating to Vite.
app.on(
'certificate-error',
(event, webContents, url, error, certificate, callback) => {
@@ -201,6 +202,10 @@ function initializeApp(): void {
// https://github.com/electron/electron/blob/v17.2.0/docs/tutorial/security.md#12-verify-webview-options-before-creation
app.on('web-contents-created', (_, contents) => {
contents.on('will-navigate', (event, navigationUrl) => {
// Allow reloading the renderer app in dev mode.
if (settings.dev && new URL(navigationUrl).host === 'localhost:8080') {
return;
}
logger.warn(`Navigation to ${navigationUrl} blocked by 'will-navigate'`);
event.preventDefault();
});
@@ -36,13 +36,13 @@ const RESOURCES_PATH = app.isPackaged
: path.join(__dirname, '../../../../');
const TSH_BIN_ENV_VAR = 'CONNECT_TSH_BIN_PATH';
// __dirname of this file in dev mode is webapps/packages/teleterm/build/app/dist/main
// We default to webapps/../teleport/build/tsh.
// __dirname of this file in dev mode is teleport/web/packages/teleterm/build/app/main
// We default to teleport/build/tsh.
// prettier-ignore
const TSH_BIN_DEFAULT_PATH_FOR_DEV = path.resolve(
__dirname,
'..', '..', '..', '..', '..', '..', '..', '..',
'teleport', 'build', 'tsh',
'..', '..', '..', '..', '..', '..',
'build', 'tsh',
);
// Refer to the docs of RuntimeSettings type for an explanation behind dev, debug and insecure.
@@ -121,7 +121,7 @@ export class WindowsManager {
contextIsolation: true,
nodeIntegration: false,
sandbox: false,
preload: path.join(__dirname, 'preload.js'),
preload: path.join(__dirname, '../preload/index.js'),
},
});
@@ -27,6 +27,11 @@ import {
PtyEventStart,
PtyServerEvent,
} from 'teleterm/sharedProcess/ptyHost';
import {
ptyEventOneOfIsData,
ptyEventOneOfIsExit,
ptyEventOneOfIsStartError,
} from 'teleterm/helpers';
export class PtyEventsStreamHandler {
private logger: Logger;
@@ -46,23 +51,34 @@ export class PtyEventsStreamHandler {
this.logger.info('Start');
this.writeOrThrow(
new PtyClientEvent().setStart(
new PtyEventStart().setColumns(columns).setRows(rows)
)
PtyClientEvent.create({
event: {
oneofKind: 'start',
start: PtyEventStart.create({ columns, rows }),
},
})
);
}
write(data: string): void {
this.writeOrThrow(
new PtyClientEvent().setData(new PtyEventData().setMessage(data))
PtyClientEvent.create({
event: {
oneofKind: 'data',
data: PtyEventData.create({ message: data }),
},
})
);
}
resize(columns: number, rows: number): void {
this.writeOrThrow(
new PtyClientEvent().setResize(
new PtyEventResize().setColumns(columns).setRows(rows)
)
PtyClientEvent.create({
event: {
oneofKind: 'resize',
resize: PtyEventResize.create({ columns, rows }),
},
})
);
}
@@ -80,7 +96,7 @@ export class PtyEventsStreamHandler {
onOpen(callback: () => void): RemoveListenerFunction {
return this.addDataListenerAndReturnRemovalFunction(
(event: PtyServerEvent) => {
if (event.hasOpen()) {
if (event.event.oneofKind === 'open') {
callback();
}
}
@@ -90,8 +106,8 @@ export class PtyEventsStreamHandler {
onData(callback: (data: string) => void): RemoveListenerFunction {
return this.addDataListenerAndReturnRemovalFunction(
(event: PtyServerEvent) => {
if (event.hasData()) {
callback(event.getData().getMessage());
if (ptyEventOneOfIsData(event.event)) {
callback(event.event.data.message);
}
}
);
@@ -102,9 +118,9 @@ export class PtyEventsStreamHandler {
): RemoveListenerFunction {
return this.addDataListenerAndReturnRemovalFunction(
(event: PtyServerEvent) => {
if (event.hasExit()) {
this.logger.info('On exit', event.getExit().toObject());
callback(event.getExit().toObject());
if (ptyEventOneOfIsExit(event.event)) {
this.logger.info('On exit', event.event.exit);
callback(event.event.exit);
}
}
);
@@ -113,12 +129,9 @@ export class PtyEventsStreamHandler {
onStartError(callback: (message: string) => void): RemoveListenerFunction {
return this.addDataListenerAndReturnRemovalFunction(
(event: PtyServerEvent) => {
if (event.hasStartError()) {
this.logger.info(
'On start error',
event.getStartError().toObject().message
);
callback(event.getStartError().toObject().message);
if (ptyEventOneOfIsStartError(event.event)) {
this.logger.info('On start error', event.event.startError.message);
callback(event.event.startError.message);
}
}
);
@@ -17,7 +17,6 @@
*/
import { ChannelCredentials, Metadata } from '@grpc/grpc-js';
import { Struct } from 'google-protobuf/google/protobuf/struct_pb';
import {
PtyHostClient as GrpcClient,
@@ -25,6 +24,8 @@ import {
PtyId,
} from 'teleterm/sharedProcess/ptyHost';
import { Struct } from 'teleterm/sharedProcess/api/protogen/google/protobuf/struct_pb';
import { PtyHostClient } from '../types';
import { PtyEventsStreamHandler } from './ptyEventsStreamHandler';
@@ -36,16 +37,17 @@ export function createPtyHostClient(
const client = new GrpcClient(address, credentials);
return {
createPtyProcess(ptyOptions) {
const request = new PtyCreate()
.setArgsList(ptyOptions.args)
.setPath(ptyOptions.path)
.setEnv(Struct.fromJavaScript(ptyOptions.env));
const request = PtyCreate.create({
args: ptyOptions.args,
path: ptyOptions.path,
env: Struct.fromJson(ptyOptions.env),
});
if (ptyOptions.cwd) {
request.setCwd(ptyOptions.cwd);
request.cwd = ptyOptions.cwd;
}
if (ptyOptions.initMessage) {
request.setInitMessage(ptyOptions.initMessage);
request.initMessage = ptyOptions.initMessage;
}
return new Promise<string>((resolve, reject) => {
@@ -53,18 +55,18 @@ export function createPtyHostClient(
if (error) {
reject(error);
} else {
resolve(response.toObject().id);
resolve(response.id);
}
});
});
},
getCwd(ptyId) {
return new Promise((resolve, reject) => {
client.getCwd(new PtyId().setId(ptyId), (error, response) => {
client.getCwd(PtyId.create({ id: ptyId }), (error, response) => {
if (error) {
reject(error);
} else {
resolve(response.toObject().cwd);
resolve(response.cwd);
}
});
});
File diff suppressed because it is too large Load Diff
@@ -18,8 +18,8 @@
import { ClientReadableStream } from '@grpc/grpc-js';
import { FileTransferListeners } from 'shared/components/FileTransfer';
import { FileTransferProgress } from 'gen-proto-js/teleport/lib/teleterm/v1/service_pb';
import * as api from 'gen-proto-js/teleport/lib/teleterm/v1/service_pb';
import { FileTransferProgress } from 'gen-proto-ts/teleport/lib/teleterm/v1/service_pb';
import * as api from 'gen-proto-ts/teleport/lib/teleterm/v1/service_pb';
import { TshAbortSignal } from './types';
@@ -32,7 +32,7 @@ export function createFileTransferStream(
return {
onProgress(callback: (progress: number) => void) {
stream.on('data', (data: api.FileTransferProgress) =>
callback(data.getPercentage())
callback(data.percentage)
);
},
onComplete(callback: () => void) {
@@ -20,12 +20,12 @@ import { getCliCommandArgs, getCliCommandEnv } from './gateway';
import { GatewayCLICommand } from './types';
describe('getCliCommandArgs', () => {
it("extracts Node.js-style args from cliCommand's argsList", () => {
it("extracts Node.js-style args from cliCommand's args", () => {
const cliCommand = makeCliCommand();
const args = getCliCommandArgs(cliCommand);
expect(args).toEqual([cliCommand.argsList[1]]);
expect(args).toEqual([cliCommand.args[1]]);
});
});
@@ -43,8 +43,8 @@ describe('getCliCommandEnv', () => {
const makeCliCommand = (): GatewayCLICommand => {
return {
path: '/Users/foo/Applications/psql.app/MacOS/psql',
argsList: ['psql', 'localhost:1337'],
envList: ['foo=bar', 'baz=quux'],
args: ['psql', 'localhost:1337'],
env: ['foo=bar', 'baz=quux'],
preview: 'foo=bar baz=quux psql localhost:1337',
};
};
@@ -27,7 +27,7 @@ import { GatewayCLICommand } from './types';
* include just the args.
*/
export function getCliCommandArgs(cliCommand: GatewayCLICommand): string[] {
const [, ...args] = cliCommand.argsList;
const [, ...args] = cliCommand.args;
return args;
}
@@ -36,7 +36,7 @@ export function getCliCommandArgs(cliCommand: GatewayCLICommand): string[] {
* We are safe to use this as the presentational command name.
*/
export function getCliCommandArgv0(cliCommand: GatewayCLICommand): string {
return cliCommand.argsList[0];
return cliCommand.args[0];
}
/**
@@ -48,7 +48,7 @@ export function getCliCommandEnv(
cliCommand: GatewayCLICommand
): Record<string, string> {
return Object.fromEntries(
cliCommand.envList.map(nameEqualsValue => nameEqualsValue.split('='))
cliCommand.env.map(nameEqualsValue => nameEqualsValue.split('='))
);
}
@@ -16,9 +16,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import * as api from 'gen-proto-js/teleport/lib/teleterm/v1/usage_events_pb';
import * as prehogApi from 'gen-proto-js/prehog/v1alpha/connect_pb';
import * as api from 'gen-proto-ts/teleport/lib/teleterm/v1/usage_events_pb';
import * as prehogApi from 'gen-proto-ts/prehog/v1alpha/connect_pb';
import {
connectEventOneOfIsAccessRequestAssumeRole,
connectEventOneOfIsAccessRequestCreate,
connectEventOneOfIsAccessRequestReview,
connectEventOneOfIsClusterLogin,
connectEventOneOfIsConnectMyComputerAgentStart,
connectEventOneOfIsConnectMyComputerSetup,
connectEventOneOfIsFileTransferRun,
connectEventOneOfIsProtocolUse,
} from 'teleterm/helpers';
import * as types from './types';
@@ -26,103 +36,162 @@ import * as types from './types';
* Maps a plain JS object into a gRPC request object.
*/
export function mapUsageEvent(event: types.ReportUsageEventRequest) {
return new api.ReportUsageEventRequest()
.setAuthClusterId(event.authClusterId)
.setPrehogReq(mapPrehogBody(event.prehogReq));
return api.ReportUsageEventRequest.create({
authClusterId: event.authClusterId,
prehogReq: mapPrehogBody(event.prehogReq),
});
}
function mapPrehogBody(
plainReq: types.ReportUsageEventRequest['prehogReq']
): prehogApi.SubmitConnectEventRequest {
const req = new prehogApi.SubmitConnectEventRequest()
.setTimestamp(Timestamp.fromDate(plainReq.timestamp))
.setDistinctId(plainReq.distinctId);
if (!plainReq) {
throw new Error(`Unrecognized event: ${JSON.stringify(plainReq)}`);
}
const req = prehogApi.SubmitConnectEventRequest.create({
timestamp: plainReq.timestamp,
distinctId: plainReq.distinctId,
});
// Non-anonymized events.
if (plainReq.userJobRoleUpdate) {
const event = plainReq.userJobRoleUpdate;
const reqEvent = new prehogApi.ConnectUserJobRoleUpdateEvent().setJobRole(
event.jobRole
);
if (plainReq.event.oneofKind === 'userJobRoleUpdate') {
const event = plainReq.event.userJobRoleUpdate;
const reqEvent = prehogApi.ConnectUserJobRoleUpdateEvent.create({
jobRole: event.jobRole,
});
return req.setUserJobRoleUpdate(reqEvent);
req.event = {
oneofKind: 'userJobRoleUpdate',
userJobRoleUpdate: reqEvent,
};
return req;
}
// Anonymized events.
if (plainReq.clusterLogin) {
const event = plainReq.clusterLogin;
const reqEvent = new prehogApi.ConnectClusterLoginEvent()
.setClusterName(event.clusterName)
.setUserName(event.userName)
.setConnectorType(event.connectorType)
.setOs(event.os)
.setArch(event.arch)
.setOsVersion(event.osVersion)
.setAppVersion(event.appVersion);
if (connectEventOneOfIsClusterLogin(plainReq.event)) {
const event = plainReq.event.clusterLogin;
const reqEvent = prehogApi.ConnectClusterLoginEvent.create({
clusterName: event.clusterName,
userName: event.userName,
connectorType: event.connectorType,
os: event.os,
arch: event.arch,
osVersion: event.osVersion,
appVersion: event.appVersion,
});
return req.setClusterLogin(reqEvent);
req.event = {
oneofKind: 'clusterLogin',
clusterLogin: reqEvent,
};
return req;
}
if (plainReq.protocolUse) {
const event = plainReq.protocolUse;
const reqEvent = new prehogApi.ConnectProtocolUseEvent()
.setClusterName(event.clusterName)
.setUserName(event.userName)
.setProtocol(event.protocol)
.setOrigin(event.origin);
if (connectEventOneOfIsProtocolUse(plainReq.event)) {
const event = plainReq.event.protocolUse;
const reqEvent = prehogApi.ConnectProtocolUseEvent.create({
clusterName: event.clusterName,
userName: event.userName,
protocol: event.protocol,
origin: event.origin,
});
return req.setProtocolUse(reqEvent);
req.event = {
oneofKind: 'protocolUse',
protocolUse: reqEvent,
};
return req;
}
if (plainReq.accessRequestCreate) {
const event = plainReq.accessRequestCreate;
const reqEvent = new prehogApi.ConnectAccessRequestCreateEvent()
.setClusterName(event.clusterName)
.setUserName(event.userName)
.setKind(event.kind);
if (connectEventOneOfIsAccessRequestCreate(plainReq.event)) {
const event = plainReq.event.accessRequestCreate;
const reqEvent = prehogApi.ConnectAccessRequestCreateEvent.create({
clusterName: event.clusterName,
userName: event.userName,
kind: event.kind,
});
return req.setAccessRequestCreate(reqEvent);
req.event = {
oneofKind: 'accessRequestCreate',
accessRequestCreate: reqEvent,
};
return req;
}
if (plainReq.accessRequestReview) {
const event = plainReq.accessRequestReview;
const reqEvent = new prehogApi.ConnectAccessRequestReviewEvent()
.setClusterName(event.clusterName)
.setUserName(event.userName);
if (connectEventOneOfIsAccessRequestReview(plainReq.event)) {
const event = plainReq.event.accessRequestReview;
const reqEvent = prehogApi.ConnectAccessRequestReviewEvent.create({
clusterName: event.clusterName,
userName: event.userName,
});
return req.setAccessRequestReview(reqEvent);
req.event = {
oneofKind: 'accessRequestReview',
accessRequestReview: reqEvent,
};
return req;
}
if (plainReq.accessRequestAssumeRole) {
const event = plainReq.accessRequestAssumeRole;
const reqEvent = new prehogApi.ConnectAccessRequestAssumeRoleEvent()
.setClusterName(event.clusterName)
.setUserName(event.userName);
if (connectEventOneOfIsAccessRequestAssumeRole(plainReq.event)) {
const event = plainReq.event.accessRequestAssumeRole;
const reqEvent = prehogApi.ConnectAccessRequestAssumeRoleEvent.create({
clusterName: event.clusterName,
userName: event.userName,
});
return req.setAccessRequestAssumeRole(reqEvent);
req.event = {
oneofKind: 'accessRequestAssumeRole',
accessRequestAssumeRole: reqEvent,
};
return req;
}
if (plainReq.fileTransferRun) {
const event = plainReq.fileTransferRun;
const reqEvent = new prehogApi.ConnectFileTransferRunEvent()
.setClusterName(event.clusterName)
.setUserName(event.userName)
.setIsUpload(event.isUpload);
if (connectEventOneOfIsFileTransferRun(plainReq.event)) {
const event = plainReq.event.fileTransferRun;
const reqEvent = prehogApi.ConnectFileTransferRunEvent.create({
clusterName: event.clusterName,
userName: event.userName,
isUpload: event.isUpload,
});
return req.setFileTransferRun(reqEvent);
req.event = {
oneofKind: 'fileTransferRun',
fileTransferRun: reqEvent,
};
return req;
}
if (plainReq.connectMyComputerSetup) {
const event = plainReq.connectMyComputerSetup;
const reqEvent = new prehogApi.ConnectConnectMyComputerSetup()
.setClusterName(event.clusterName)
.setUserName(event.userName)
.setSuccess(event.success)
.setFailedStep(event.failedStep);
if (connectEventOneOfIsConnectMyComputerSetup(plainReq.event)) {
const event = plainReq.event.connectMyComputerSetup;
const reqEvent = prehogApi.ConnectConnectMyComputerSetup.create({
clusterName: event.clusterName,
userName: event.userName,
success: event.success,
failedStep: event.failedStep,
});
return req.setConnectMyComputerSetup(reqEvent);
req.event = {
oneofKind: 'connectMyComputerSetup',
connectMyComputerSetup: reqEvent,
};
return req;
}
if (plainReq.connectMyComputerAgentStart) {
const event = plainReq.connectMyComputerAgentStart;
const reqEvent = new prehogApi.ConnectConnectMyComputerAgentStart()
.setClusterName(event.clusterName)
.setUserName(event.userName);
if (connectEventOneOfIsConnectMyComputerAgentStart(plainReq.event)) {
const event = plainReq.event.connectMyComputerAgentStart;
const reqEvent = prehogApi.ConnectConnectMyComputerAgentStart.create({
clusterName: event.clusterName,
userName: event.userName,
});
return req.setConnectMyComputerAgentStart(reqEvent);
req.event = {
oneofKind: 'connectMyComputerAgentStart',
connectMyComputerAgentStart: reqEvent,
};
return req;
}
throw new Error(`Unrecognized event: ${JSON.stringify(plainReq)}`);
@@ -26,7 +26,7 @@ export const makeServer = (props: Partial<tsh.Server> = {}): tsh.Server => ({
name: '1234abcd-1234-abcd-1234-abcd1234abcd',
hostname: 'foo',
addr: '127.0.0.1:3022',
labelsList: [],
labels: [],
subKind: 'teleport',
...props,
});
@@ -44,20 +44,20 @@ export const makeDatabase = (
desc: '',
hostname: '',
addr: '',
labelsList: [],
labels: [],
...props,
});
export const makeKube = (props: Partial<tsh.Kube> = {}): tsh.Kube => ({
name: 'foo',
labelsList: [],
labels: [],
uri: '/clusters/bar/kubes/foo',
...props,
});
export const makeApp = (props: Partial<tsh.App> = {}): App => ({
name: 'foo',
labelsList: [],
labels: [],
endpointUri: 'tcp://localhost:3000',
friendlyName: '',
desc: '',
@@ -104,7 +104,7 @@ export const makeLeafCluster = (
export const makeLoggedInUser = (
props: Partial<tsh.LoggedInUser> = {}
): tsh.LoggedInUser => ({
activeRequestsList: [],
activeRequests: [],
assumedRequests: {},
name: 'alice',
acl: {
@@ -113,7 +113,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
activeSessions: {
@@ -121,7 +121,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
authConnectors: {
@@ -129,7 +129,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
roles: {
@@ -137,7 +137,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
users: {
@@ -145,7 +145,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
trustedClusters: {
@@ -153,7 +153,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
events: {
@@ -161,7 +161,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
tokens: {
@@ -169,7 +169,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
servers: {
@@ -177,7 +177,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
apps: {
@@ -185,7 +185,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
dbs: {
@@ -193,7 +193,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
kubeservers: {
@@ -201,7 +201,7 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
accessRequests: {
@@ -209,15 +209,15 @@ export const makeLoggedInUser = (
read: true,
edit: true,
create: true,
pb_delete: true,
delete: true,
use: true,
},
},
sshLoginsList: [],
rolesList: [],
requestableRolesList: [],
suggestedReviewersList: [],
userType: tsh.UserType.USER_TYPE_LOCAL,
sshLogins: [],
roles: [],
requestableRoles: [],
suggestedReviewers: [],
userType: tsh.UserType.LOCAL,
...props,
});
@@ -233,8 +233,8 @@ export const makeDatabaseGateway = (
protocol: 'postgres',
gatewayCliCommand: {
path: '/foo/psql',
argsList: ['psql', 'localhost:1337'],
envList: [],
args: ['psql', 'localhost:1337'],
env: [],
preview: 'psql localhost:1337',
},
targetSubresourceName: 'bar',
@@ -253,8 +253,8 @@ export const makeKubeGateway = (
protocol: '',
gatewayCliCommand: {
path: '/bin/kubectl',
argsList: ['version'],
envList: ['KUBECONFIG=/path/to/kubeconfig'],
args: ['version'],
env: ['KUBECONFIG=/path/to/kubeconfig'],
preview: 'KUBECONFIG=/path/to/kubeconfig /bin/kubectl version',
},
targetSubresourceName: '',
@@ -273,8 +273,8 @@ export const makeAppGateway = (
gatewayCliCommand: {
path: '',
preview: 'curl http://localhost:1337',
envList: [],
argsList: [],
env: [],
args: [],
},
targetUser: '',
protocol: 'HTTP',
@@ -24,40 +24,38 @@ import { RequestState } from 'e-teleport/services/workflow';
import { SortType } from 'design/DataTable/types';
import { FileTransferListeners } from 'shared/components/FileTransfer';
import { NodeSubKind } from 'shared/services';
import apiCluster from 'gen-proto-js/teleport/lib/teleterm/v1/cluster_pb';
import apiDb from 'gen-proto-js/teleport/lib/teleterm/v1/database_pb';
import apiGateway from 'gen-proto-js/teleport/lib/teleterm/v1/gateway_pb';
import apiServer from 'gen-proto-js/teleport/lib/teleterm/v1/server_pb';
import apiKube from 'gen-proto-js/teleport/lib/teleterm/v1/kube_pb';
import apiApp from 'gen-proto-js/teleport/lib/teleterm/v1/app_pb';
import apiLabel from 'gen-proto-js/teleport/lib/teleterm/v1/label_pb';
import apiService, {
FileTransferDirection,
HeadlessAuthenticationState,
} from 'gen-proto-js/teleport/lib/teleterm/v1/service_pb';
import apiAuthSettings from 'gen-proto-js/teleport/lib/teleterm/v1/auth_settings_pb';
import apiAccessRequest from 'gen-proto-js/teleport/lib/teleterm/v1/access_request_pb';
import apiUsageEvents from 'gen-proto-js/teleport/lib/teleterm/v1/usage_events_pb';
import apiAccessList from 'gen-proto-js/teleport/accesslist/v1/accesslist_pb';
import * as apiCluster from 'gen-proto-ts/teleport/lib/teleterm/v1/cluster_pb';
import * as apiDb from 'gen-proto-ts/teleport/lib/teleterm/v1/database_pb';
import * as apiGateway from 'gen-proto-ts/teleport/lib/teleterm/v1/gateway_pb';
import * as apiServer from 'gen-proto-ts/teleport/lib/teleterm/v1/server_pb';
import * as apiKube from 'gen-proto-ts/teleport/lib/teleterm/v1/kube_pb';
import * as apiApp from 'gen-proto-ts/teleport/lib/teleterm/v1/app_pb';
import * as apiLabel from 'gen-proto-ts/teleport/lib/teleterm/v1/label_pb';
import * as apiService from 'gen-proto-ts/teleport/lib/teleterm/v1/service_pb';
import * as apiAuthSettings from 'gen-proto-ts/teleport/lib/teleterm/v1/auth_settings_pb';
import * as apiAccessRequest from 'gen-proto-ts/teleport/lib/teleterm/v1/access_request_pb';
import * as apiUsageEvents from 'gen-proto-ts/teleport/lib/teleterm/v1/usage_events_pb';
import * as apiAccessList from 'gen-proto-ts/teleport/accesslist/v1/accesslist_pb';
import * as uri from 'teleterm/ui/uri';
// We want to reexport both the type and the value of UserType. Because it's in a namespace, we have
// to alias it first to do the reexport.
// https://www.typescriptlang.org/docs/handbook/namespaces.html#aliases
import UserType = apiCluster.LoggedInUser.UserType;
import UserType = apiCluster.LoggedInUser_UserType;
export { UserType };
export interface Kube extends apiKube.Kube.AsObject {
export interface Kube extends apiKube.Kube {
uri: uri.KubeUri;
}
export interface Server extends apiServer.Server.AsObject {
export interface Server extends apiServer.Server {
uri: uri.ServerUri;
subKind: NodeSubKind;
}
export interface App extends apiApp.App.AsObject {
export interface App extends apiApp.App {
uri: uri.AppUri;
/** Name of the application. */
name: string;
@@ -86,7 +84,7 @@ export interface App extends apiApp.App.AsObject {
samlApp: boolean;
}
export interface Gateway extends apiGateway.Gateway.AsObject {
export interface Gateway extends apiGateway.Gateway {
uri: uri.GatewayUri;
targetUri: uri.GatewayTargetUri;
// The type of gatewayCliCommand was repeated here just to refer to the type with the JSDoc.
@@ -107,43 +105,33 @@ export interface Gateway extends apiGateway.Gateway.AsObject {
* if the user was to invoke it manually from the terminal. Should not be actually used to execute
* anything in the shell.
*/
export type GatewayCLICommand = apiGateway.GatewayCLICommand.AsObject;
export type GatewayCLICommand = apiGateway.GatewayCLICommand;
export type AccessRequest = apiAccessRequest.AccessRequest.AsObject;
export type ResourceId = apiAccessRequest.ResourceID.AsObject;
export type AccessRequestReview = apiAccessRequest.AccessRequestReview.AsObject;
export type AccessList = apiAccessList.AccessList.AsObject;
export type AccessRequest = apiAccessRequest.AccessRequest;
export type ResourceId = apiAccessRequest.ResourceID;
export type AccessRequestReview = apiAccessRequest.AccessRequestReview;
export type AccessList = apiAccessList.AccessList;
export interface GetServersResponse
extends apiService.GetServersResponse.AsObject {
agentsList: Server[];
export interface GetServersResponse extends apiService.GetServersResponse {
agents: Server[];
}
export interface GetDatabasesResponse
extends apiService.GetDatabasesResponse.AsObject {
agentsList: Database[];
export interface GetDatabasesResponse extends apiService.GetDatabasesResponse {
agents: Database[];
}
export interface GetKubesResponse extends apiService.GetKubesResponse.AsObject {
agentsList: Kube[];
export interface GetKubesResponse extends apiService.GetKubesResponse {
agents: Kube[];
}
export interface GetAppsResponse extends apiService.GetAppsResponse.AsObject {
agentsList: App[];
export interface GetAppsResponse extends apiService.GetAppsResponse {
agents: App[];
}
export type GetRequestableRolesResponse =
apiService.GetRequestableRolesResponse.AsObject;
apiService.GetRequestableRolesResponse;
export type ReportUsageEventRequest = Modify<
apiUsageEvents.ReportUsageEventRequest.AsObject,
{
prehogReq: Modify<
apiUsageEvents.ReportUsageEventRequest.AsObject['prehogReq'],
{ timestamp: Date }
>;
}
>;
export type ReportUsageEventRequest = apiUsageEvents.ReportUsageEventRequest;
// Available types are listed here:
// https://github.com/gravitational/teleport/blob/v9.0.3/lib/defaults/defaults.go#L513-L530
@@ -157,11 +145,11 @@ export type GatewayProtocol =
| 'redis'
| 'sqlserver';
export interface Database extends apiDb.Database.AsObject {
export interface Database extends apiDb.Database {
uri: uri.DatabaseUri;
}
export interface Cluster extends apiCluster.Cluster.AsObject {
export interface Cluster extends apiCluster.Cluster {
/**
* The URI of the cluster.
*
@@ -196,22 +184,21 @@ export interface Cluster extends apiCluster.Cluster.AsObject {
* includes a situation in which the cert has expired. If the cluster was added to the app but the
* user is yet to log in, loggedInUser is not present.
*/
export type LoggedInUser = apiCluster.LoggedInUser.AsObject & {
export type LoggedInUser = apiCluster.LoggedInUser & {
assumedRequests?: Record<string, AssumedRequest>;
/**
* acl is available only after the cluster details are fetched, as acl is not stored on disk.
*/
acl?: apiCluster.ACL.AsObject;
acl?: apiCluster.ACL;
};
export type AuthProvider = apiAuthSettings.AuthProvider.AsObject;
export type AuthSettings = apiAuthSettings.AuthSettings.AsObject;
export type AuthProvider = apiAuthSettings.AuthProvider;
export type AuthSettings = apiAuthSettings.AuthSettings;
export interface FileTransferRequest
extends apiService.FileTransferRequest.AsObject {
export interface FileTransferRequest extends apiService.FileTransferRequest {
serverUri: uri.ServerUri;
}
export type WebauthnCredentialInfo = apiService.CredentialInfo.AsObject;
export type WebauthnCredentialInfo = apiService.CredentialInfo;
export type WebauthnLoginPrompt =
| WebauthnLoginTapPrompt
| WebauthnLoginRetapPrompt
@@ -229,7 +216,7 @@ export type WebauthnLoginCredentialPrompt = {
onUserResponse(index: number): void;
};
export type LoginPasswordlessRequest =
Partial<apiService.LoginPasswordlessRequest.AsObject>;
Partial<apiService.LoginPasswordlessRequest>;
export type TshdClient = {
listRootClusters: (abortSignal?: TshAbortSignal) => Promise<Cluster[]>;
@@ -327,20 +314,20 @@ export type TshdClient = {
) => Promise<void>;
listUnifiedResources: (
params: apiService.ListUnifiedResourcesRequest.AsObject,
params: apiService.ListUnifiedResourcesRequest,
abortSignal?: TshAbortSignal
) => Promise<ListUnifiedResourcesResponse>;
getUserPreferences: (
params: apiService.GetUserPreferencesRequest.AsObject,
params: apiService.GetUserPreferencesRequest,
abortSignal?: TshAbortSignal
) => Promise<UserPreferences>;
updateUserPreferences: (
params: apiService.UpdateUserPreferencesRequest.AsObject,
params: apiService.UpdateUserPreferencesRequest,
abortSignal?: TshAbortSignal
) => Promise<UserPreferences>;
getSuggestedAccessLists: (
params: apiService.GetSuggestedAccessListsRequest.AsObject,
params: apiService.GetSuggestedAccessListsRequest,
abortSignal?: TshAbortSignal
) => Promise<AccessList[]>;
promoteAccessRequest: (
@@ -433,21 +420,19 @@ export type AssumedRequest = {
roles: string[];
};
export { FileTransferDirection };
export type Label = apiLabel.Label.AsObject;
export type Label = apiLabel.Label;
export type CreateConnectMyComputerRoleResponse =
apiService.CreateConnectMyComputerRoleResponse.AsObject;
apiService.CreateConnectMyComputerRoleResponse;
export type CreateConnectMyComputerNodeTokenResponse =
apiService.CreateConnectMyComputerNodeTokenResponse.AsObject;
apiService.CreateConnectMyComputerNodeTokenResponse;
export type WaitForConnectMyComputerNodeJoinResponse =
apiService.WaitForConnectMyComputerNodeJoinResponse.AsObject & {
apiService.WaitForConnectMyComputerNodeJoinResponse & {
server: Server;
};
export type ListUnifiedResourcesRequest =
apiService.ListUnifiedResourcesRequest.AsObject;
apiService.ListUnifiedResourcesRequest;
export type ListUnifiedResourcesResponse = {
resources: UnifiedResourceResponse[];
nextKey: string;
@@ -461,19 +446,14 @@ export type UnifiedResourceResponse =
| { kind: 'kube'; resource: Kube }
| { kind: 'app'; resource: App };
export type UserPreferences = apiService.UserPreferences.AsObject;
export type UserPreferences = apiService.UserPreferences;
export type PromoteAccessRequestParams =
apiService.PromoteAccessRequestRequest.AsObject & {
apiService.PromoteAccessRequestRequest & {
rootClusterUri: uri.RootClusterUri;
};
// Replaces object property with a new type
type Modify<T, R> = Omit<T, keyof R> & R;
export type UpdateHeadlessAuthenticationStateParams = {
rootClusterUri: uri.RootClusterUri;
headlessAuthenticationId: string;
state: apiService.HeadlessAuthenticationState;
};
export { HeadlessAuthenticationState };
@@ -17,40 +17,42 @@
*/
import * as grpc from '@grpc/grpc-js';
import * as api from 'gen-proto-js/teleport/lib/teleterm/v1/tshd_events_service_pb';
import * as apiService from 'gen-proto-js/teleport/lib/teleterm/v1/tshd_events_service_grpc_pb';
import * as protobuf from 'google-protobuf';
import * as api from 'gen-proto-ts/teleport/lib/teleterm/v1/tshd_events_service_pb';
import * as apiService from 'gen-proto-ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server';
import * as uri from 'teleterm/ui/uri';
import Logger from 'teleterm/logger';
import { TshdEventContextBridgeService } from 'teleterm/types';
import {
ExtractRequestType,
ExtractResponseType,
TshdEventContextBridgeService,
} from 'teleterm/types';
import { filterSensitiveProperties } from 'teleterm/services/tshd/middleware';
export interface ReloginRequest extends api.ReloginRequest.AsObject {
export interface ReloginRequest extends api.ReloginRequest {
rootClusterUri: uri.RootClusterUri;
gatewayCertExpired?: GatewayCertExpired;
}
export interface GatewayCertExpired extends api.GatewayCertExpired.AsObject {
export interface GatewayCertExpired extends api.GatewayCertExpired {
gatewayUri: uri.GatewayUri;
targetUri: uri.DatabaseUri;
}
export interface SendNotificationRequest
extends api.SendNotificationRequest.AsObject {
export interface SendNotificationRequest extends api.SendNotificationRequest {
cannotProxyGatewayConnection?: CannotProxyGatewayConnection;
}
export interface CannotProxyGatewayConnection
extends api.CannotProxyGatewayConnection.AsObject {
extends api.CannotProxyGatewayConnection {
gatewayUri: uri.GatewayUri;
targetUri: uri.DatabaseUri;
}
export type PromptMfaRequest = api.PromptMFARequest.AsObject & {
export type PromptMfaRequest = api.PromptMFARequest & {
rootClusterUri: uri.RootClusterUri;
};
export interface SendPendingHeadlessAuthenticationRequest
extends api.SendPendingHeadlessAuthenticationRequest.AsObject {
extends api.SendPendingHeadlessAuthenticationRequest {
rootClusterUri: uri.RootClusterUri;
}
@@ -77,17 +79,7 @@ export async function createTshdEventsServer(
);
const { service, setupTshdEventContextBridgeService } = createService(logger);
server.addService(
apiService.TshdEventsServiceService,
// Whatever we use for generating protobufs generated wrong types. The types say that
// server.addService expects an UntypedServiceImplementation as the second argument.
// ITshdEventsServiceService does implement UntypedServiceImplementation.
//
// However, what we actually need to pass as the second argument needs to have the shape of
// ITshdEventsServiceServer. That's why we ignore the error below.
// @ts-expect-error The generated protobuf types seem to be wrong.
service
);
server.addService(apiService.tshdEventsServiceDefinition, service);
return { resolvedAddress, setupTshdEventContextBridgeService };
}
@@ -134,7 +126,7 @@ async function createServer(
* See the JSDoc for TshdEventContextBridgeService for more details.
*/
function createService(logger: Logger): {
service: apiService.ITshdEventsServiceServer;
service: apiService.ITshdEventsService;
setupTshdEventContextBridgeService: (
listener: TshdEventContextBridgeService
) => void;
@@ -155,18 +147,20 @@ function createService(logger: Logger): {
* through the context bridge) to a class instance (as expected by grpc-js).
*/
function processEvent<
RpcName extends keyof apiService.ITshdEventsServiceServer,
Request extends protobuf.Message,
Response extends protobuf.Message
RpcName extends keyof apiService.ITshdEventsService,
Request extends ExtractRequestType<
Parameters<apiService.ITshdEventsService[RpcName]>[0]
>,
Response extends ExtractResponseType<
Parameters<apiService.ITshdEventsService[RpcName]>[1]
>
>(
rpcName: RpcName,
call: grpc.ServerUnaryCall<Request, Response>,
callback: (error: Error, response: Response) => void,
mapResponseObjectToResponseInstance: (
responseObject: ReturnType<Response['toObject']>
) => Response
callback: (error: Error | null, response: Response | null) => void,
mapResponseObjectToResponseInstance: (responseObject: Response) => Response
) {
const request = call.request.toObject();
const request = call.request;
logger.info(`got ${rpcName}`, filterSensitiveProperties(request));
@@ -190,12 +184,7 @@ function createService(logger: Logger): {
throw new Error(`No context bridge handler for ${rpcName}`);
}
contextBridgeHandler({
// `as` is a workaround. We'd have to tell TypeScript somehow that `Request` is the same
// between `contextBridgeService` and `processEvent`, but it's not clear how to achieve that.
request: request as ReturnType<Request['toObject']>,
onRequestCancelled,
}).then(
contextBridgeHandler({ request, onRequestCancelled }).then(
response => {
if (call.cancelled) {
return;
@@ -232,29 +221,25 @@ function createService(logger: Logger): {
);
}
const service: apiService.ITshdEventsServiceServer = {
const service: apiService.ITshdEventsService = {
relogin: (call, callback) =>
processEvent('relogin', call, callback, () => new api.ReloginResponse()),
processEvent('relogin', call, callback, () =>
api.ReloginResponse.create()
),
sendNotification: (call, callback) =>
processEvent(
'sendNotification',
call,
callback,
() => new api.SendNotificationResponse()
processEvent('sendNotification', call, callback, () =>
api.SendNotificationResponse.create()
),
sendPendingHeadlessAuthentication: (call, callback) =>
processEvent(
'sendPendingHeadlessAuthentication',
call,
callback,
() => new api.SendPendingHeadlessAuthenticationResponse()
processEvent('sendPendingHeadlessAuthentication', call, callback, () =>
api.SendPendingHeadlessAuthenticationResponse.create()
),
promptMFA: (call, callback) => {
processEvent('promptMFA', call, callback, response =>
new api.PromptMFAResponse().setTotpCode(response.totpCode)
api.PromptMFAResponse.create({ totpCode: response?.totpCode })
);
},
};
@@ -0,0 +1,484 @@
/* eslint-disable */
// @generated by protobuf-ts 2.9.3 with parameter long_type_number,eslint_disable,add_pb_suffix,client_grpc1,server_grpc1,ts_nocheck
// @generated from protobuf file "google/protobuf/struct.proto" (package "google.protobuf", syntax proto3)
// tslint:disable
// @ts-nocheck
//
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
import type { BinaryWriteOptions } from "@protobuf-ts/runtime";
import type { IBinaryWriter } from "@protobuf-ts/runtime";
import { WireType } from "@protobuf-ts/runtime";
import type { BinaryReadOptions } from "@protobuf-ts/runtime";
import type { IBinaryReader } from "@protobuf-ts/runtime";
import { UnknownFieldHandler } from "@protobuf-ts/runtime";
import type { PartialMessage } from "@protobuf-ts/runtime";
import { reflectionMergePartial } from "@protobuf-ts/runtime";
import { isJsonObject } from "@protobuf-ts/runtime";
import { typeofJsonValue } from "@protobuf-ts/runtime";
import type { JsonValue } from "@protobuf-ts/runtime";
import type { JsonReadOptions } from "@protobuf-ts/runtime";
import type { JsonWriteOptions } from "@protobuf-ts/runtime";
import type { JsonObject } from "@protobuf-ts/runtime";
import { MessageType } from "@protobuf-ts/runtime";
/**
* `Struct` represents a structured data value, consisting of fields
* which map to dynamically typed values. In some languages, `Struct`
* might be supported by a native representation. For example, in
* scripting languages like JS a struct is represented as an
* object. The details of that representation are described together
* with the proto support for the language.
*
* The JSON representation for `Struct` is JSON object.
*
* @generated from protobuf message google.protobuf.Struct
*/
export interface Struct {
/**
* Unordered map of dynamically typed values.
*
* @generated from protobuf field: map<string, google.protobuf.Value> fields = 1;
*/
fields: {
[key: string]: Value;
};
}
/**
* `Value` represents a dynamically typed value which can be either
* null, a number, a string, a boolean, a recursive struct value, or a
* list of values. A producer of value is expected to set one of these
* variants. Absence of any variant indicates an error.
*
* The JSON representation for `Value` is JSON value.
*
* @generated from protobuf message google.protobuf.Value
*/
export interface Value {
/**
* @generated from protobuf oneof: kind
*/
kind: {
oneofKind: "nullValue";
/**
* Represents a null value.
*
* @generated from protobuf field: google.protobuf.NullValue null_value = 1;
*/
nullValue: NullValue;
} | {
oneofKind: "numberValue";
/**
* Represents a double value.
*
* @generated from protobuf field: double number_value = 2;
*/
numberValue: number;
} | {
oneofKind: "stringValue";
/**
* Represents a string value.
*
* @generated from protobuf field: string string_value = 3;
*/
stringValue: string;
} | {
oneofKind: "boolValue";
/**
* Represents a boolean value.
*
* @generated from protobuf field: bool bool_value = 4;
*/
boolValue: boolean;
} | {
oneofKind: "structValue";
/**
* Represents a structured value.
*
* @generated from protobuf field: google.protobuf.Struct struct_value = 5;
*/
structValue: Struct;
} | {
oneofKind: "listValue";
/**
* Represents a repeated `Value`.
*
* @generated from protobuf field: google.protobuf.ListValue list_value = 6;
*/
listValue: ListValue;
} | {
oneofKind: undefined;
};
}
/**
* `ListValue` is a wrapper around a repeated field of values.
*
* The JSON representation for `ListValue` is JSON array.
*
* @generated from protobuf message google.protobuf.ListValue
*/
export interface ListValue {
/**
* Repeated field of dynamically typed values.
*
* @generated from protobuf field: repeated google.protobuf.Value values = 1;
*/
values: Value[];
}
/**
* `NullValue` is a singleton enumeration to represent the null value for the
* `Value` type union.
*
* The JSON representation for `NullValue` is JSON `null`.
*
* @generated from protobuf enum google.protobuf.NullValue
*/
export enum NullValue {
/**
* Null value.
*
* @generated from protobuf enum value: NULL_VALUE = 0;
*/
NULL_VALUE = 0
}
// @generated message type with reflection information, may provide speed optimized methods
class Struct$Type extends MessageType<Struct> {
constructor() {
super("google.protobuf.Struct", [
{ no: 1, name: "fields", kind: "map", K: 9 /*ScalarType.STRING*/, V: { kind: "message", T: () => Value } }
]);
}
/**
* Encode `Struct` to JSON object.
*/
internalJsonWrite(message: Struct, options: JsonWriteOptions): JsonValue {
let json: JsonObject = {};
for (let [k, v] of Object.entries(message.fields)) {
json[k] = Value.toJson(v);
}
return json;
}
/**
* Decode `Struct` from JSON object.
*/
internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: Struct): Struct {
if (!isJsonObject(json))
throw new globalThis.Error("Unable to parse message " + this.typeName + " from JSON " + typeofJsonValue(json) + ".");
if (!target)
target = this.create();
for (let [k, v] of globalThis.Object.entries(json)) {
target.fields[k] = Value.fromJson(v);
}
return target;
}
create(value?: PartialMessage<Struct>): Struct {
const message = globalThis.Object.create((this.messagePrototype!));
message.fields = {};
if (value !== undefined)
reflectionMergePartial<Struct>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Struct): Struct {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* map<string, google.protobuf.Value> fields */ 1:
this.binaryReadMap1(message.fields, reader, options);
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
private binaryReadMap1(map: Struct["fields"], reader: IBinaryReader, options: BinaryReadOptions): void {
let len = reader.uint32(), end = reader.pos + len, key: keyof Struct["fields"] | undefined, val: Struct["fields"][any] | undefined;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case 1:
key = reader.string();
break;
case 2:
val = Value.internalBinaryRead(reader, reader.uint32(), options);
break;
default: throw new globalThis.Error("unknown map entry field for field google.protobuf.Struct.fields");
}
}
map[key ?? ""] = val ?? Value.create();
}
internalBinaryWrite(message: Struct, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* map<string, google.protobuf.Value> fields = 1; */
for (let k of globalThis.Object.keys(message.fields)) {
writer.tag(1, WireType.LengthDelimited).fork().tag(1, WireType.LengthDelimited).string(k);
writer.tag(2, WireType.LengthDelimited).fork();
Value.internalBinaryWrite(message.fields[k], writer, options);
writer.join().join();
}
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message google.protobuf.Struct
*/
export const Struct = new Struct$Type();
// @generated message type with reflection information, may provide speed optimized methods
class Value$Type extends MessageType<Value> {
constructor() {
super("google.protobuf.Value", [
{ no: 1, name: "null_value", kind: "enum", oneof: "kind", T: () => ["google.protobuf.NullValue", NullValue] },
{ no: 2, name: "number_value", kind: "scalar", oneof: "kind", T: 1 /*ScalarType.DOUBLE*/ },
{ no: 3, name: "string_value", kind: "scalar", oneof: "kind", T: 9 /*ScalarType.STRING*/ },
{ no: 4, name: "bool_value", kind: "scalar", oneof: "kind", T: 8 /*ScalarType.BOOL*/ },
{ no: 5, name: "struct_value", kind: "message", oneof: "kind", T: () => Struct },
{ no: 6, name: "list_value", kind: "message", oneof: "kind", T: () => ListValue }
]);
}
/**
* Encode `Value` to JSON value.
*/
internalJsonWrite(message: Value, options: JsonWriteOptions): JsonValue {
if (message.kind.oneofKind === undefined)
throw new globalThis.Error();
switch (message.kind.oneofKind) {
case undefined: throw new globalThis.Error();
case "boolValue": return message.kind.boolValue;
case "nullValue": return null;
case "numberValue":
let numberValue = message.kind.numberValue;
if (typeof numberValue == "number" && !Number.isFinite(numberValue))
throw new globalThis.Error();
return numberValue;
case "stringValue": return message.kind.stringValue;
case "listValue":
let listValueField = this.fields.find(f => f.no === 6);
if (listValueField?.kind !== "message")
throw new globalThis.Error();
return listValueField.T().toJson(message.kind.listValue);
case "structValue":
let structValueField = this.fields.find(f => f.no === 5);
if (structValueField?.kind !== "message")
throw new globalThis.Error();
return structValueField.T().toJson(message.kind.structValue);
}
}
/**
* Decode `Value` from JSON value.
*/
internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: Value): Value {
if (!target)
target = this.create();
switch (typeof json) {
case "number":
target.kind = { oneofKind: "numberValue", numberValue: json };
break;
case "string":
target.kind = { oneofKind: "stringValue", stringValue: json };
break;
case "boolean":
target.kind = { oneofKind: "boolValue", boolValue: json };
break;
case "object":
if (json === null) {
target.kind = { oneofKind: "nullValue", nullValue: NullValue.NULL_VALUE };
}
else if (globalThis.Array.isArray(json)) {
target.kind = { oneofKind: "listValue", listValue: ListValue.fromJson(json) };
}
else {
target.kind = { oneofKind: "structValue", structValue: Struct.fromJson(json) };
}
break;
default: throw new globalThis.Error("Unable to parse " + this.typeName + " from JSON " + typeofJsonValue(json));
}
return target;
}
create(value?: PartialMessage<Value>): Value {
const message = globalThis.Object.create((this.messagePrototype!));
message.kind = { oneofKind: undefined };
if (value !== undefined)
reflectionMergePartial<Value>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Value): Value {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* google.protobuf.NullValue null_value */ 1:
message.kind = {
oneofKind: "nullValue",
nullValue: reader.int32()
};
break;
case /* double number_value */ 2:
message.kind = {
oneofKind: "numberValue",
numberValue: reader.double()
};
break;
case /* string string_value */ 3:
message.kind = {
oneofKind: "stringValue",
stringValue: reader.string()
};
break;
case /* bool bool_value */ 4:
message.kind = {
oneofKind: "boolValue",
boolValue: reader.bool()
};
break;
case /* google.protobuf.Struct struct_value */ 5:
message.kind = {
oneofKind: "structValue",
structValue: Struct.internalBinaryRead(reader, reader.uint32(), options, (message.kind as any).structValue)
};
break;
case /* google.protobuf.ListValue list_value */ 6:
message.kind = {
oneofKind: "listValue",
listValue: ListValue.internalBinaryRead(reader, reader.uint32(), options, (message.kind as any).listValue)
};
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: Value, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* google.protobuf.NullValue null_value = 1; */
if (message.kind.oneofKind === "nullValue")
writer.tag(1, WireType.Varint).int32(message.kind.nullValue);
/* double number_value = 2; */
if (message.kind.oneofKind === "numberValue")
writer.tag(2, WireType.Bit64).double(message.kind.numberValue);
/* string string_value = 3; */
if (message.kind.oneofKind === "stringValue")
writer.tag(3, WireType.LengthDelimited).string(message.kind.stringValue);
/* bool bool_value = 4; */
if (message.kind.oneofKind === "boolValue")
writer.tag(4, WireType.Varint).bool(message.kind.boolValue);
/* google.protobuf.Struct struct_value = 5; */
if (message.kind.oneofKind === "structValue")
Struct.internalBinaryWrite(message.kind.structValue, writer.tag(5, WireType.LengthDelimited).fork(), options).join();
/* google.protobuf.ListValue list_value = 6; */
if (message.kind.oneofKind === "listValue")
ListValue.internalBinaryWrite(message.kind.listValue, writer.tag(6, WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message google.protobuf.Value
*/
export const Value = new Value$Type();
// @generated message type with reflection information, may provide speed optimized methods
class ListValue$Type extends MessageType<ListValue> {
constructor() {
super("google.protobuf.ListValue", [
{ no: 1, name: "values", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => Value }
]);
}
/**
* Encode `ListValue` to JSON array.
*/
internalJsonWrite(message: ListValue, options: JsonWriteOptions): JsonValue {
return message.values.map(v => Value.toJson(v));
}
/**
* Decode `ListValue` from JSON array.
*/
internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: ListValue): ListValue {
if (!globalThis.Array.isArray(json))
throw new globalThis.Error("Unable to parse " + this.typeName + " from JSON " + typeofJsonValue(json));
if (!target)
target = this.create();
let values = json.map(v => Value.fromJson(v));
target.values.push(...values);
return target;
}
create(value?: PartialMessage<ListValue>): ListValue {
const message = globalThis.Object.create((this.messagePrototype!));
message.values = [];
if (value !== undefined)
reflectionMergePartial<ListValue>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ListValue): ListValue {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* repeated google.protobuf.Value values */ 1:
message.values.push(Value.internalBinaryRead(reader, reader.uint32(), options));
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: ListValue, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* repeated google.protobuf.Value values = 1; */
for (let i = 0; i < message.values.length; i++)
Value.internalBinaryWrite(message.values[i], writer.tag(1, WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message google.protobuf.ListValue
*/
export const ListValue = new ListValue$Type();
@@ -1,93 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// package:
// file: ptyHostService.proto
/* tslint:disable */
/* eslint-disable */
import * as grpc from "grpc";
import * as ptyHostService_pb from "./ptyHostService_pb";
import * as google_protobuf_struct_pb from "google-protobuf/google/protobuf/struct_pb";
interface IPtyHostService extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {
createPtyProcess: IPtyHostService_ICreatePtyProcess;
exchangeEvents: IPtyHostService_IExchangeEvents;
getCwd: IPtyHostService_IGetCwd;
}
interface IPtyHostService_ICreatePtyProcess extends grpc.MethodDefinition<ptyHostService_pb.PtyCreate, ptyHostService_pb.PtyId> {
path: "/PtyHost/CreatePtyProcess";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<ptyHostService_pb.PtyCreate>;
requestDeserialize: grpc.deserialize<ptyHostService_pb.PtyCreate>;
responseSerialize: grpc.serialize<ptyHostService_pb.PtyId>;
responseDeserialize: grpc.deserialize<ptyHostService_pb.PtyId>;
}
interface IPtyHostService_IExchangeEvents extends grpc.MethodDefinition<ptyHostService_pb.PtyClientEvent, ptyHostService_pb.PtyServerEvent> {
path: "/PtyHost/ExchangeEvents";
requestStream: true;
responseStream: true;
requestSerialize: grpc.serialize<ptyHostService_pb.PtyClientEvent>;
requestDeserialize: grpc.deserialize<ptyHostService_pb.PtyClientEvent>;
responseSerialize: grpc.serialize<ptyHostService_pb.PtyServerEvent>;
responseDeserialize: grpc.deserialize<ptyHostService_pb.PtyServerEvent>;
}
interface IPtyHostService_IGetCwd extends grpc.MethodDefinition<ptyHostService_pb.PtyId, ptyHostService_pb.PtyCwd> {
path: "/PtyHost/GetCwd";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<ptyHostService_pb.PtyId>;
requestDeserialize: grpc.deserialize<ptyHostService_pb.PtyId>;
responseSerialize: grpc.serialize<ptyHostService_pb.PtyCwd>;
responseDeserialize: grpc.deserialize<ptyHostService_pb.PtyCwd>;
}
export const PtyHostService: IPtyHostService;
export interface IPtyHostServer {
createPtyProcess: grpc.handleUnaryCall<ptyHostService_pb.PtyCreate, ptyHostService_pb.PtyId>;
exchangeEvents: grpc.handleBidiStreamingCall<ptyHostService_pb.PtyClientEvent, ptyHostService_pb.PtyServerEvent>;
getCwd: grpc.handleUnaryCall<ptyHostService_pb.PtyId, ptyHostService_pb.PtyCwd>;
}
export interface IPtyHostClient {
createPtyProcess(request: ptyHostService_pb.PtyCreate, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyId) => void): grpc.ClientUnaryCall;
createPtyProcess(request: ptyHostService_pb.PtyCreate, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyId) => void): grpc.ClientUnaryCall;
createPtyProcess(request: ptyHostService_pb.PtyCreate, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyId) => void): grpc.ClientUnaryCall;
exchangeEvents(): grpc.ClientDuplexStream<ptyHostService_pb.PtyClientEvent, ptyHostService_pb.PtyServerEvent>;
exchangeEvents(options: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<ptyHostService_pb.PtyClientEvent, ptyHostService_pb.PtyServerEvent>;
exchangeEvents(metadata: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<ptyHostService_pb.PtyClientEvent, ptyHostService_pb.PtyServerEvent>;
getCwd(request: ptyHostService_pb.PtyId, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyCwd) => void): grpc.ClientUnaryCall;
getCwd(request: ptyHostService_pb.PtyId, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyCwd) => void): grpc.ClientUnaryCall;
getCwd(request: ptyHostService_pb.PtyId, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyCwd) => void): grpc.ClientUnaryCall;
}
export class PtyHostClient extends grpc.Client implements IPtyHostClient {
constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);
public createPtyProcess(request: ptyHostService_pb.PtyCreate, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyId) => void): grpc.ClientUnaryCall;
public createPtyProcess(request: ptyHostService_pb.PtyCreate, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyId) => void): grpc.ClientUnaryCall;
public createPtyProcess(request: ptyHostService_pb.PtyCreate, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyId) => void): grpc.ClientUnaryCall;
public exchangeEvents(options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<ptyHostService_pb.PtyClientEvent, ptyHostService_pb.PtyServerEvent>;
public exchangeEvents(metadata?: grpc.Metadata, options?: Partial<grpc.CallOptions>): grpc.ClientDuplexStream<ptyHostService_pb.PtyClientEvent, ptyHostService_pb.PtyServerEvent>;
public getCwd(request: ptyHostService_pb.PtyId, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyCwd) => void): grpc.ClientUnaryCall;
public getCwd(request: ptyHostService_pb.PtyId, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyCwd) => void): grpc.ClientUnaryCall;
public getCwd(request: ptyHostService_pb.PtyId, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: ptyHostService_pb.PtyCwd) => void): grpc.ClientUnaryCall;
}
@@ -1,122 +0,0 @@
// GENERATED CODE -- DO NOT EDIT!
// Original file comments:
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// TODO(ravicious): Before introducing any changes, move this file to the /proto dir and
// remove the generate-grpc-shared script.
//
'use strict';
var grpc = require('@grpc/grpc-js');
var ptyHostService_pb = require('./ptyHostService_pb.js');
var google_protobuf_struct_pb = require('google-protobuf/google/protobuf/struct_pb.js');
function serialize_PtyClientEvent(arg) {
if (!(arg instanceof ptyHostService_pb.PtyClientEvent)) {
throw new Error('Expected argument of type PtyClientEvent');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_PtyClientEvent(buffer_arg) {
return ptyHostService_pb.PtyClientEvent.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_PtyCreate(arg) {
if (!(arg instanceof ptyHostService_pb.PtyCreate)) {
throw new Error('Expected argument of type PtyCreate');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_PtyCreate(buffer_arg) {
return ptyHostService_pb.PtyCreate.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_PtyCwd(arg) {
if (!(arg instanceof ptyHostService_pb.PtyCwd)) {
throw new Error('Expected argument of type PtyCwd');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_PtyCwd(buffer_arg) {
return ptyHostService_pb.PtyCwd.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_PtyId(arg) {
if (!(arg instanceof ptyHostService_pb.PtyId)) {
throw new Error('Expected argument of type PtyId');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_PtyId(buffer_arg) {
return ptyHostService_pb.PtyId.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_PtyServerEvent(arg) {
if (!(arg instanceof ptyHostService_pb.PtyServerEvent)) {
throw new Error('Expected argument of type PtyServerEvent');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_PtyServerEvent(buffer_arg) {
return ptyHostService_pb.PtyServerEvent.deserializeBinary(new Uint8Array(buffer_arg));
}
var PtyHostService = exports.PtyHostService = {
createPtyProcess: {
path: '/PtyHost/CreatePtyProcess',
requestStream: false,
responseStream: false,
requestType: ptyHostService_pb.PtyCreate,
responseType: ptyHostService_pb.PtyId,
requestSerialize: serialize_PtyCreate,
requestDeserialize: deserialize_PtyCreate,
responseSerialize: serialize_PtyId,
responseDeserialize: deserialize_PtyId,
},
exchangeEvents: {
path: '/PtyHost/ExchangeEvents',
requestStream: true,
responseStream: true,
requestType: ptyHostService_pb.PtyClientEvent,
responseType: ptyHostService_pb.PtyServerEvent,
requestSerialize: serialize_PtyClientEvent,
requestDeserialize: deserialize_PtyClientEvent,
responseSerialize: serialize_PtyServerEvent,
responseDeserialize: deserialize_PtyServerEvent,
},
getCwd: {
path: '/PtyHost/GetCwd',
requestStream: false,
responseStream: false,
requestType: ptyHostService_pb.PtyId,
responseType: ptyHostService_pb.PtyCwd,
requestSerialize: serialize_PtyId,
requestDeserialize: deserialize_PtyId,
responseSerialize: serialize_PtyCwd,
responseDeserialize: deserialize_PtyCwd,
},
};
exports.PtyHostClient = grpc.makeGenericClientConstructor(PtyHostService);
@@ -1,336 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// package:
// file: ptyHostService.proto
/* tslint:disable */
/* eslint-disable */
import * as jspb from "google-protobuf";
import * as google_protobuf_struct_pb from "google-protobuf/google/protobuf/struct_pb";
export class PtyId extends jspb.Message {
getId(): string;
setId(value: string): PtyId;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyId.AsObject;
static toObject(includeInstance: boolean, msg: PtyId): PtyId.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyId, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyId;
static deserializeBinaryFromReader(message: PtyId, reader: jspb.BinaryReader): PtyId;
}
export namespace PtyId {
export type AsObject = {
id: string,
}
}
export class PtyCreate extends jspb.Message {
getPath(): string;
setPath(value: string): PtyCreate;
clearArgsList(): void;
getArgsList(): Array<string>;
setArgsList(value: Array<string>): PtyCreate;
addArgs(value: string, index?: number): string;
getCwd(): string;
setCwd(value: string): PtyCreate;
hasEnv(): boolean;
clearEnv(): void;
getEnv(): google_protobuf_struct_pb.Struct | undefined;
setEnv(value?: google_protobuf_struct_pb.Struct): PtyCreate;
getInitMessage(): string;
setInitMessage(value: string): PtyCreate;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyCreate.AsObject;
static toObject(includeInstance: boolean, msg: PtyCreate): PtyCreate.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyCreate, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyCreate;
static deserializeBinaryFromReader(message: PtyCreate, reader: jspb.BinaryReader): PtyCreate;
}
export namespace PtyCreate {
export type AsObject = {
path: string,
argsList: Array<string>,
cwd: string,
env?: google_protobuf_struct_pb.Struct.AsObject,
initMessage: string,
}
}
export class PtyClientEvent extends jspb.Message {
hasStart(): boolean;
clearStart(): void;
getStart(): PtyEventStart | undefined;
setStart(value?: PtyEventStart): PtyClientEvent;
hasResize(): boolean;
clearResize(): void;
getResize(): PtyEventResize | undefined;
setResize(value?: PtyEventResize): PtyClientEvent;
hasData(): boolean;
clearData(): void;
getData(): PtyEventData | undefined;
setData(value?: PtyEventData): PtyClientEvent;
getEventCase(): PtyClientEvent.EventCase;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyClientEvent.AsObject;
static toObject(includeInstance: boolean, msg: PtyClientEvent): PtyClientEvent.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyClientEvent, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyClientEvent;
static deserializeBinaryFromReader(message: PtyClientEvent, reader: jspb.BinaryReader): PtyClientEvent;
}
export namespace PtyClientEvent {
export type AsObject = {
start?: PtyEventStart.AsObject,
resize?: PtyEventResize.AsObject,
data?: PtyEventData.AsObject,
}
export enum EventCase {
EVENT_NOT_SET = 0,
START = 2,
RESIZE = 3,
DATA = 4,
}
}
export class PtyServerEvent extends jspb.Message {
hasResize(): boolean;
clearResize(): void;
getResize(): PtyEventResize | undefined;
setResize(value?: PtyEventResize): PtyServerEvent;
hasData(): boolean;
clearData(): void;
getData(): PtyEventData | undefined;
setData(value?: PtyEventData): PtyServerEvent;
hasOpen(): boolean;
clearOpen(): void;
getOpen(): PtyEventOpen | undefined;
setOpen(value?: PtyEventOpen): PtyServerEvent;
hasExit(): boolean;
clearExit(): void;
getExit(): PtyEventExit | undefined;
setExit(value?: PtyEventExit): PtyServerEvent;
hasStartError(): boolean;
clearStartError(): void;
getStartError(): PtyEventStartError | undefined;
setStartError(value?: PtyEventStartError): PtyServerEvent;
getEventCase(): PtyServerEvent.EventCase;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyServerEvent.AsObject;
static toObject(includeInstance: boolean, msg: PtyServerEvent): PtyServerEvent.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyServerEvent, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyServerEvent;
static deserializeBinaryFromReader(message: PtyServerEvent, reader: jspb.BinaryReader): PtyServerEvent;
}
export namespace PtyServerEvent {
export type AsObject = {
resize?: PtyEventResize.AsObject,
data?: PtyEventData.AsObject,
open?: PtyEventOpen.AsObject,
exit?: PtyEventExit.AsObject,
startError?: PtyEventStartError.AsObject,
}
export enum EventCase {
EVENT_NOT_SET = 0,
RESIZE = 1,
DATA = 2,
OPEN = 3,
EXIT = 4,
START_ERROR = 5,
}
}
export class PtyEventStart extends jspb.Message {
getColumns(): number;
setColumns(value: number): PtyEventStart;
getRows(): number;
setRows(value: number): PtyEventStart;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyEventStart.AsObject;
static toObject(includeInstance: boolean, msg: PtyEventStart): PtyEventStart.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyEventStart, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyEventStart;
static deserializeBinaryFromReader(message: PtyEventStart, reader: jspb.BinaryReader): PtyEventStart;
}
export namespace PtyEventStart {
export type AsObject = {
columns: number,
rows: number,
}
}
export class PtyEventData extends jspb.Message {
getMessage(): string;
setMessage(value: string): PtyEventData;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyEventData.AsObject;
static toObject(includeInstance: boolean, msg: PtyEventData): PtyEventData.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyEventData, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyEventData;
static deserializeBinaryFromReader(message: PtyEventData, reader: jspb.BinaryReader): PtyEventData;
}
export namespace PtyEventData {
export type AsObject = {
message: string,
}
}
export class PtyEventResize extends jspb.Message {
getColumns(): number;
setColumns(value: number): PtyEventResize;
getRows(): number;
setRows(value: number): PtyEventResize;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyEventResize.AsObject;
static toObject(includeInstance: boolean, msg: PtyEventResize): PtyEventResize.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyEventResize, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyEventResize;
static deserializeBinaryFromReader(message: PtyEventResize, reader: jspb.BinaryReader): PtyEventResize;
}
export namespace PtyEventResize {
export type AsObject = {
columns: number,
rows: number,
}
}
export class PtyEventOpen extends jspb.Message {
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyEventOpen.AsObject;
static toObject(includeInstance: boolean, msg: PtyEventOpen): PtyEventOpen.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyEventOpen, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyEventOpen;
static deserializeBinaryFromReader(message: PtyEventOpen, reader: jspb.BinaryReader): PtyEventOpen;
}
export namespace PtyEventOpen {
export type AsObject = {
}
}
export class PtyEventExit extends jspb.Message {
getExitCode(): number;
setExitCode(value: number): PtyEventExit;
hasSignal(): boolean;
clearSignal(): void;
getSignal(): number | undefined;
setSignal(value: number): PtyEventExit;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyEventExit.AsObject;
static toObject(includeInstance: boolean, msg: PtyEventExit): PtyEventExit.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyEventExit, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyEventExit;
static deserializeBinaryFromReader(message: PtyEventExit, reader: jspb.BinaryReader): PtyEventExit;
}
export namespace PtyEventExit {
export type AsObject = {
exitCode: number,
signal?: number,
}
}
export class PtyEventStartError extends jspb.Message {
getMessage(): string;
setMessage(value: string): PtyEventStartError;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyEventStartError.AsObject;
static toObject(includeInstance: boolean, msg: PtyEventStartError): PtyEventStartError.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyEventStartError, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyEventStartError;
static deserializeBinaryFromReader(message: PtyEventStartError, reader: jspb.BinaryReader): PtyEventStartError;
}
export namespace PtyEventStartError {
export type AsObject = {
message: string,
}
}
export class PtyCwd extends jspb.Message {
getCwd(): string;
setCwd(value: string): PtyCwd;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PtyCwd.AsObject;
static toObject(includeInstance: boolean, msg: PtyCwd): PtyCwd.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: PtyCwd, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): PtyCwd;
static deserializeBinaryFromReader(message: PtyCwd, reader: jspb.BinaryReader): PtyCwd;
}
export namespace PtyCwd {
export type AsObject = {
cwd: string,
}
}
@@ -0,0 +1,91 @@
/* eslint-disable */
// @generated by protobuf-ts 2.9.3 with parameter long_type_number,eslint_disable,add_pb_suffix,client_grpc1,server_grpc1,ts_nocheck
// @generated from protobuf file "ptyHostService.proto" (syntax proto3)
// tslint:disable
// @ts-nocheck
//
//
// Teleport
// Copyright (C) 2023 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//
// TODO(ravicious): Before introducing any changes, move this file to the /proto dir and
// remove the generate-grpc-shared script.
//
import { PtyHost } from "./ptyHostService_pb";
import type { BinaryWriteOptions } from "@protobuf-ts/runtime";
import type { BinaryReadOptions } from "@protobuf-ts/runtime";
import type { PtyCwd } from "./ptyHostService_pb";
import type { PtyServerEvent } from "./ptyHostService_pb";
import type { PtyClientEvent } from "./ptyHostService_pb";
import type { PtyId } from "./ptyHostService_pb";
import type { PtyCreate } from "./ptyHostService_pb";
import * as grpc from "@grpc/grpc-js";
/**
* @generated from protobuf service PtyHost
*/
export interface IPtyHostClient {
/**
* @generated from protobuf rpc: CreatePtyProcess(PtyCreate) returns (PtyId);
*/
createPtyProcess(input: PtyCreate, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: PtyId) => void): grpc.ClientUnaryCall;
createPtyProcess(input: PtyCreate, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: PtyId) => void): grpc.ClientUnaryCall;
createPtyProcess(input: PtyCreate, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: PtyId) => void): grpc.ClientUnaryCall;
createPtyProcess(input: PtyCreate, callback: (err: grpc.ServiceError | null, value?: PtyId) => void): grpc.ClientUnaryCall;
/**
* @generated from protobuf rpc: ExchangeEvents(stream PtyClientEvent) returns (stream PtyServerEvent);
*/
exchangeEvents(metadata: grpc.Metadata, options?: grpc.CallOptions): grpc.ClientDuplexStream<PtyClientEvent, PtyServerEvent>;
exchangeEvents(options?: grpc.CallOptions): grpc.ClientDuplexStream<PtyClientEvent, PtyServerEvent>;
/**
* @generated from protobuf rpc: GetCwd(PtyId) returns (PtyCwd);
*/
getCwd(input: PtyId, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: PtyCwd) => void): grpc.ClientUnaryCall;
getCwd(input: PtyId, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: PtyCwd) => void): grpc.ClientUnaryCall;
getCwd(input: PtyId, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: PtyCwd) => void): grpc.ClientUnaryCall;
getCwd(input: PtyId, callback: (err: grpc.ServiceError | null, value?: PtyCwd) => void): grpc.ClientUnaryCall;
}
/**
* @generated from protobuf service PtyHost
*/
export class PtyHostClient extends grpc.Client implements IPtyHostClient {
private readonly _binaryOptions: Partial<BinaryReadOptions & BinaryWriteOptions>;
constructor(address: string, credentials: grpc.ChannelCredentials, options: grpc.ClientOptions = {}, binaryOptions: Partial<BinaryReadOptions & BinaryWriteOptions> = {}) {
super(address, credentials, options);
this._binaryOptions = binaryOptions;
}
/**
* @generated from protobuf rpc: CreatePtyProcess(PtyCreate) returns (PtyId);
*/
createPtyProcess(input: PtyCreate, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: PtyId) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: PtyId) => void), callback?: ((err: grpc.ServiceError | null, value?: PtyId) => void)): grpc.ClientUnaryCall {
const method = PtyHost.methods[0];
return this.makeUnaryRequest<PtyCreate, PtyId>(`/${PtyHost.typeName}/${method.name}`, (value: PtyCreate): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): PtyId => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any));
}
/**
* @generated from protobuf rpc: ExchangeEvents(stream PtyClientEvent) returns (stream PtyServerEvent);
*/
exchangeEvents(metadata?: grpc.Metadata | grpc.CallOptions, options?: grpc.CallOptions): grpc.ClientDuplexStream<PtyClientEvent, PtyServerEvent> {
const method = PtyHost.methods[1];
return this.makeBidiStreamRequest<PtyClientEvent, PtyServerEvent>(`/${PtyHost.typeName}/${method.name}`, (value: PtyClientEvent): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): PtyServerEvent => method.O.fromBinary(value, this._binaryOptions), (metadata as any), options);
}
/**
* @generated from protobuf rpc: GetCwd(PtyId) returns (PtyCwd);
*/
getCwd(input: PtyId, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: PtyCwd) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: PtyCwd) => void), callback?: ((err: grpc.ServiceError | null, value?: PtyCwd) => void)): grpc.ClientUnaryCall {
const method = PtyHost.methods[2];
return this.makeUnaryRequest<PtyId, PtyCwd>(`/${PtyHost.typeName}/${method.name}`, (value: PtyId): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): PtyCwd => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any));
}
}
@@ -0,0 +1,93 @@
/* eslint-disable */
// @generated by protobuf-ts 2.9.3 with parameter long_type_number,eslint_disable,add_pb_suffix,client_grpc1,server_grpc1,ts_nocheck
// @generated from protobuf file "ptyHostService.proto" (syntax proto3)
// tslint:disable
// @ts-nocheck
//
//
// Teleport
// Copyright (C) 2023 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//
// TODO(ravicious): Before introducing any changes, move this file to the /proto dir and
// remove the generate-grpc-shared script.
//
import { PtyCwd } from "./ptyHostService_pb";
import { PtyServerEvent } from "./ptyHostService_pb";
import { PtyClientEvent } from "./ptyHostService_pb";
import { PtyId } from "./ptyHostService_pb";
import { PtyCreate } from "./ptyHostService_pb";
import type * as grpc from "@grpc/grpc-js";
/**
* @generated from protobuf service PtyHost
*/
export interface IPtyHost extends grpc.UntypedServiceImplementation {
/**
* @generated from protobuf rpc: CreatePtyProcess(PtyCreate) returns (PtyId);
*/
createPtyProcess: grpc.handleUnaryCall<PtyCreate, PtyId>;
/**
* @generated from protobuf rpc: ExchangeEvents(stream PtyClientEvent) returns (stream PtyServerEvent);
*/
exchangeEvents: grpc.handleBidiStreamingCall<PtyClientEvent, PtyServerEvent>;
/**
* @generated from protobuf rpc: GetCwd(PtyId) returns (PtyCwd);
*/
getCwd: grpc.handleUnaryCall<PtyId, PtyCwd>;
}
/**
* @grpc/grpc-js definition for the protobuf service PtyHost.
*
* Usage: Implement the interface IPtyHost and add to a grpc server.
*
* ```typescript
* const server = new grpc.Server();
* const service: IPtyHost = ...
* server.addService(ptyHostDefinition, service);
* ```
*/
export const ptyHostDefinition: grpc.ServiceDefinition<IPtyHost> = {
createPtyProcess: {
path: "/PtyHost/CreatePtyProcess",
originalName: "CreatePtyProcess",
requestStream: false,
responseStream: false,
responseDeserialize: bytes => PtyId.fromBinary(bytes),
requestDeserialize: bytes => PtyCreate.fromBinary(bytes),
responseSerialize: value => Buffer.from(PtyId.toBinary(value)),
requestSerialize: value => Buffer.from(PtyCreate.toBinary(value))
},
exchangeEvents: {
path: "/PtyHost/ExchangeEvents",
originalName: "ExchangeEvents",
requestStream: true,
responseStream: true,
responseDeserialize: bytes => PtyServerEvent.fromBinary(bytes),
requestDeserialize: bytes => PtyClientEvent.fromBinary(bytes),
responseSerialize: value => Buffer.from(PtyServerEvent.toBinary(value)),
requestSerialize: value => Buffer.from(PtyClientEvent.toBinary(value))
},
getCwd: {
path: "/PtyHost/GetCwd",
originalName: "GetCwd",
requestStream: false,
responseStream: false,
responseDeserialize: bytes => PtyCwd.fromBinary(bytes),
requestDeserialize: bytes => PtyId.fromBinary(bytes),
responseSerialize: value => Buffer.from(PtyCwd.toBinary(value)),
requestSerialize: value => Buffer.from(PtyId.toBinary(value))
}
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,836 @@
/* eslint-disable */
// @generated by protobuf-ts 2.9.3 with parameter long_type_number,eslint_disable,add_pb_suffix,client_grpc1,server_grpc1,ts_nocheck
// @generated from protobuf file "ptyHostService.proto" (syntax proto3)
// tslint:disable
// @ts-nocheck
//
//
// Teleport
// Copyright (C) 2023 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//
// TODO(ravicious): Before introducing any changes, move this file to the /proto dir and
// remove the generate-grpc-shared script.
//
import { ServiceType } from "@protobuf-ts/runtime-rpc";
import type { BinaryWriteOptions } from "@protobuf-ts/runtime";
import type { IBinaryWriter } from "@protobuf-ts/runtime";
import { WireType } from "@protobuf-ts/runtime";
import type { BinaryReadOptions } from "@protobuf-ts/runtime";
import type { IBinaryReader } from "@protobuf-ts/runtime";
import { UnknownFieldHandler } from "@protobuf-ts/runtime";
import type { PartialMessage } from "@protobuf-ts/runtime";
import { reflectionMergePartial } from "@protobuf-ts/runtime";
import { MessageType } from "@protobuf-ts/runtime";
import { Struct } from "./google/protobuf/struct_pb";
/**
* @generated from protobuf message PtyId
*/
export interface PtyId {
/**
* @generated from protobuf field: string id = 1;
*/
id: string;
}
/**
* @generated from protobuf message PtyCreate
*/
export interface PtyCreate {
/**
* @generated from protobuf field: string path = 3;
*/
path: string;
/**
* @generated from protobuf field: repeated string args = 4;
*/
args: string[];
/**
* @generated from protobuf field: string cwd = 5;
*/
cwd: string;
/**
* @generated from protobuf field: google.protobuf.Struct env = 7;
*/
env?: Struct;
/**
* @generated from protobuf field: string init_message = 8;
*/
initMessage: string;
}
/**
* @generated from protobuf message PtyClientEvent
*/
export interface PtyClientEvent {
/**
* @generated from protobuf oneof: event
*/
event: {
oneofKind: "start";
/**
* @generated from protobuf field: PtyEventStart start = 2;
*/
start: PtyEventStart;
} | {
oneofKind: "resize";
/**
* @generated from protobuf field: PtyEventResize resize = 3;
*/
resize: PtyEventResize;
} | {
oneofKind: "data";
/**
* @generated from protobuf field: PtyEventData data = 4;
*/
data: PtyEventData;
} | {
oneofKind: undefined;
};
}
/**
* @generated from protobuf message PtyServerEvent
*/
export interface PtyServerEvent {
/**
* @generated from protobuf oneof: event
*/
event: {
oneofKind: "resize";
/**
* @generated from protobuf field: PtyEventResize resize = 1;
*/
resize: PtyEventResize;
} | {
oneofKind: "data";
/**
* @generated from protobuf field: PtyEventData data = 2;
*/
data: PtyEventData;
} | {
oneofKind: "open";
/**
* @generated from protobuf field: PtyEventOpen open = 3;
*/
open: PtyEventOpen;
} | {
oneofKind: "exit";
/**
* @generated from protobuf field: PtyEventExit exit = 4;
*/
exit: PtyEventExit;
} | {
oneofKind: "startError";
/**
* @generated from protobuf field: PtyEventStartError start_error = 5;
*/
startError: PtyEventStartError;
} | {
oneofKind: undefined;
};
}
/**
* @generated from protobuf message PtyEventStart
*/
export interface PtyEventStart {
/**
* @generated from protobuf field: uint32 columns = 2;
*/
columns: number;
/**
* @generated from protobuf field: uint32 rows = 3;
*/
rows: number;
}
/**
* @generated from protobuf message PtyEventData
*/
export interface PtyEventData {
/**
* @generated from protobuf field: string message = 2;
*/
message: string;
}
/**
* @generated from protobuf message PtyEventResize
*/
export interface PtyEventResize {
/**
* @generated from protobuf field: uint32 columns = 2;
*/
columns: number;
/**
* @generated from protobuf field: uint32 rows = 3;
*/
rows: number;
}
/**
* @generated from protobuf message PtyEventOpen
*/
export interface PtyEventOpen {
}
/**
* @generated from protobuf message PtyEventExit
*/
export interface PtyEventExit {
/**
* @generated from protobuf field: uint32 exit_code = 1;
*/
exitCode: number;
/**
* @generated from protobuf field: optional uint32 signal = 2;
*/
signal?: number;
}
/**
* @generated from protobuf message PtyEventStartError
*/
export interface PtyEventStartError {
/**
* @generated from protobuf field: string message = 1;
*/
message: string;
}
/**
* @generated from protobuf message PtyCwd
*/
export interface PtyCwd {
/**
* @generated from protobuf field: string cwd = 1;
*/
cwd: string;
}
// @generated message type with reflection information, may provide speed optimized methods
class PtyId$Type extends MessageType<PtyId> {
constructor() {
super("PtyId", [
{ no: 1, name: "id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
]);
}
create(value?: PartialMessage<PtyId>): PtyId {
const message = globalThis.Object.create((this.messagePrototype!));
message.id = "";
if (value !== undefined)
reflectionMergePartial<PtyId>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyId): PtyId {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string id */ 1:
message.id = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyId, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* string id = 1; */
if (message.id !== "")
writer.tag(1, WireType.LengthDelimited).string(message.id);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyId
*/
export const PtyId = new PtyId$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyCreate$Type extends MessageType<PtyCreate> {
constructor() {
super("PtyCreate", [
{ no: 3, name: "path", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 4, name: "args", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ },
{ no: 5, name: "cwd", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 7, name: "env", kind: "message", T: () => Struct },
{ no: 8, name: "init_message", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
]);
}
create(value?: PartialMessage<PtyCreate>): PtyCreate {
const message = globalThis.Object.create((this.messagePrototype!));
message.path = "";
message.args = [];
message.cwd = "";
message.initMessage = "";
if (value !== undefined)
reflectionMergePartial<PtyCreate>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyCreate): PtyCreate {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string path */ 3:
message.path = reader.string();
break;
case /* repeated string args */ 4:
message.args.push(reader.string());
break;
case /* string cwd */ 5:
message.cwd = reader.string();
break;
case /* google.protobuf.Struct env */ 7:
message.env = Struct.internalBinaryRead(reader, reader.uint32(), options, message.env);
break;
case /* string init_message */ 8:
message.initMessage = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyCreate, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* string path = 3; */
if (message.path !== "")
writer.tag(3, WireType.LengthDelimited).string(message.path);
/* repeated string args = 4; */
for (let i = 0; i < message.args.length; i++)
writer.tag(4, WireType.LengthDelimited).string(message.args[i]);
/* string cwd = 5; */
if (message.cwd !== "")
writer.tag(5, WireType.LengthDelimited).string(message.cwd);
/* google.protobuf.Struct env = 7; */
if (message.env)
Struct.internalBinaryWrite(message.env, writer.tag(7, WireType.LengthDelimited).fork(), options).join();
/* string init_message = 8; */
if (message.initMessage !== "")
writer.tag(8, WireType.LengthDelimited).string(message.initMessage);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyCreate
*/
export const PtyCreate = new PtyCreate$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyClientEvent$Type extends MessageType<PtyClientEvent> {
constructor() {
super("PtyClientEvent", [
{ no: 2, name: "start", kind: "message", oneof: "event", T: () => PtyEventStart },
{ no: 3, name: "resize", kind: "message", oneof: "event", T: () => PtyEventResize },
{ no: 4, name: "data", kind: "message", oneof: "event", T: () => PtyEventData }
]);
}
create(value?: PartialMessage<PtyClientEvent>): PtyClientEvent {
const message = globalThis.Object.create((this.messagePrototype!));
message.event = { oneofKind: undefined };
if (value !== undefined)
reflectionMergePartial<PtyClientEvent>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyClientEvent): PtyClientEvent {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* PtyEventStart start */ 2:
message.event = {
oneofKind: "start",
start: PtyEventStart.internalBinaryRead(reader, reader.uint32(), options, (message.event as any).start)
};
break;
case /* PtyEventResize resize */ 3:
message.event = {
oneofKind: "resize",
resize: PtyEventResize.internalBinaryRead(reader, reader.uint32(), options, (message.event as any).resize)
};
break;
case /* PtyEventData data */ 4:
message.event = {
oneofKind: "data",
data: PtyEventData.internalBinaryRead(reader, reader.uint32(), options, (message.event as any).data)
};
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyClientEvent, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* PtyEventStart start = 2; */
if (message.event.oneofKind === "start")
PtyEventStart.internalBinaryWrite(message.event.start, writer.tag(2, WireType.LengthDelimited).fork(), options).join();
/* PtyEventResize resize = 3; */
if (message.event.oneofKind === "resize")
PtyEventResize.internalBinaryWrite(message.event.resize, writer.tag(3, WireType.LengthDelimited).fork(), options).join();
/* PtyEventData data = 4; */
if (message.event.oneofKind === "data")
PtyEventData.internalBinaryWrite(message.event.data, writer.tag(4, WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyClientEvent
*/
export const PtyClientEvent = new PtyClientEvent$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyServerEvent$Type extends MessageType<PtyServerEvent> {
constructor() {
super("PtyServerEvent", [
{ no: 1, name: "resize", kind: "message", oneof: "event", T: () => PtyEventResize },
{ no: 2, name: "data", kind: "message", oneof: "event", T: () => PtyEventData },
{ no: 3, name: "open", kind: "message", oneof: "event", T: () => PtyEventOpen },
{ no: 4, name: "exit", kind: "message", oneof: "event", T: () => PtyEventExit },
{ no: 5, name: "start_error", kind: "message", oneof: "event", T: () => PtyEventStartError }
]);
}
create(value?: PartialMessage<PtyServerEvent>): PtyServerEvent {
const message = globalThis.Object.create((this.messagePrototype!));
message.event = { oneofKind: undefined };
if (value !== undefined)
reflectionMergePartial<PtyServerEvent>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyServerEvent): PtyServerEvent {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* PtyEventResize resize */ 1:
message.event = {
oneofKind: "resize",
resize: PtyEventResize.internalBinaryRead(reader, reader.uint32(), options, (message.event as any).resize)
};
break;
case /* PtyEventData data */ 2:
message.event = {
oneofKind: "data",
data: PtyEventData.internalBinaryRead(reader, reader.uint32(), options, (message.event as any).data)
};
break;
case /* PtyEventOpen open */ 3:
message.event = {
oneofKind: "open",
open: PtyEventOpen.internalBinaryRead(reader, reader.uint32(), options, (message.event as any).open)
};
break;
case /* PtyEventExit exit */ 4:
message.event = {
oneofKind: "exit",
exit: PtyEventExit.internalBinaryRead(reader, reader.uint32(), options, (message.event as any).exit)
};
break;
case /* PtyEventStartError start_error */ 5:
message.event = {
oneofKind: "startError",
startError: PtyEventStartError.internalBinaryRead(reader, reader.uint32(), options, (message.event as any).startError)
};
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyServerEvent, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* PtyEventResize resize = 1; */
if (message.event.oneofKind === "resize")
PtyEventResize.internalBinaryWrite(message.event.resize, writer.tag(1, WireType.LengthDelimited).fork(), options).join();
/* PtyEventData data = 2; */
if (message.event.oneofKind === "data")
PtyEventData.internalBinaryWrite(message.event.data, writer.tag(2, WireType.LengthDelimited).fork(), options).join();
/* PtyEventOpen open = 3; */
if (message.event.oneofKind === "open")
PtyEventOpen.internalBinaryWrite(message.event.open, writer.tag(3, WireType.LengthDelimited).fork(), options).join();
/* PtyEventExit exit = 4; */
if (message.event.oneofKind === "exit")
PtyEventExit.internalBinaryWrite(message.event.exit, writer.tag(4, WireType.LengthDelimited).fork(), options).join();
/* PtyEventStartError start_error = 5; */
if (message.event.oneofKind === "startError")
PtyEventStartError.internalBinaryWrite(message.event.startError, writer.tag(5, WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyServerEvent
*/
export const PtyServerEvent = new PtyServerEvent$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyEventStart$Type extends MessageType<PtyEventStart> {
constructor() {
super("PtyEventStart", [
{ no: 2, name: "columns", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
{ no: 3, name: "rows", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }
]);
}
create(value?: PartialMessage<PtyEventStart>): PtyEventStart {
const message = globalThis.Object.create((this.messagePrototype!));
message.columns = 0;
message.rows = 0;
if (value !== undefined)
reflectionMergePartial<PtyEventStart>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyEventStart): PtyEventStart {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* uint32 columns */ 2:
message.columns = reader.uint32();
break;
case /* uint32 rows */ 3:
message.rows = reader.uint32();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyEventStart, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* uint32 columns = 2; */
if (message.columns !== 0)
writer.tag(2, WireType.Varint).uint32(message.columns);
/* uint32 rows = 3; */
if (message.rows !== 0)
writer.tag(3, WireType.Varint).uint32(message.rows);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyEventStart
*/
export const PtyEventStart = new PtyEventStart$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyEventData$Type extends MessageType<PtyEventData> {
constructor() {
super("PtyEventData", [
{ no: 2, name: "message", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
]);
}
create(value?: PartialMessage<PtyEventData>): PtyEventData {
const message = globalThis.Object.create((this.messagePrototype!));
message.message = "";
if (value !== undefined)
reflectionMergePartial<PtyEventData>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyEventData): PtyEventData {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string message */ 2:
message.message = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyEventData, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* string message = 2; */
if (message.message !== "")
writer.tag(2, WireType.LengthDelimited).string(message.message);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyEventData
*/
export const PtyEventData = new PtyEventData$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyEventResize$Type extends MessageType<PtyEventResize> {
constructor() {
super("PtyEventResize", [
{ no: 2, name: "columns", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
{ no: 3, name: "rows", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }
]);
}
create(value?: PartialMessage<PtyEventResize>): PtyEventResize {
const message = globalThis.Object.create((this.messagePrototype!));
message.columns = 0;
message.rows = 0;
if (value !== undefined)
reflectionMergePartial<PtyEventResize>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyEventResize): PtyEventResize {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* uint32 columns */ 2:
message.columns = reader.uint32();
break;
case /* uint32 rows */ 3:
message.rows = reader.uint32();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyEventResize, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* uint32 columns = 2; */
if (message.columns !== 0)
writer.tag(2, WireType.Varint).uint32(message.columns);
/* uint32 rows = 3; */
if (message.rows !== 0)
writer.tag(3, WireType.Varint).uint32(message.rows);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyEventResize
*/
export const PtyEventResize = new PtyEventResize$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyEventOpen$Type extends MessageType<PtyEventOpen> {
constructor() {
super("PtyEventOpen", []);
}
create(value?: PartialMessage<PtyEventOpen>): PtyEventOpen {
const message = globalThis.Object.create((this.messagePrototype!));
if (value !== undefined)
reflectionMergePartial<PtyEventOpen>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyEventOpen): PtyEventOpen {
return target ?? this.create();
}
internalBinaryWrite(message: PtyEventOpen, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyEventOpen
*/
export const PtyEventOpen = new PtyEventOpen$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyEventExit$Type extends MessageType<PtyEventExit> {
constructor() {
super("PtyEventExit", [
{ no: 1, name: "exit_code", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
{ no: 2, name: "signal", kind: "scalar", opt: true, T: 13 /*ScalarType.UINT32*/ }
]);
}
create(value?: PartialMessage<PtyEventExit>): PtyEventExit {
const message = globalThis.Object.create((this.messagePrototype!));
message.exitCode = 0;
if (value !== undefined)
reflectionMergePartial<PtyEventExit>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyEventExit): PtyEventExit {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* uint32 exit_code */ 1:
message.exitCode = reader.uint32();
break;
case /* optional uint32 signal */ 2:
message.signal = reader.uint32();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyEventExit, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* uint32 exit_code = 1; */
if (message.exitCode !== 0)
writer.tag(1, WireType.Varint).uint32(message.exitCode);
/* optional uint32 signal = 2; */
if (message.signal !== undefined)
writer.tag(2, WireType.Varint).uint32(message.signal);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyEventExit
*/
export const PtyEventExit = new PtyEventExit$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyEventStartError$Type extends MessageType<PtyEventStartError> {
constructor() {
super("PtyEventStartError", [
{ no: 1, name: "message", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
]);
}
create(value?: PartialMessage<PtyEventStartError>): PtyEventStartError {
const message = globalThis.Object.create((this.messagePrototype!));
message.message = "";
if (value !== undefined)
reflectionMergePartial<PtyEventStartError>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyEventStartError): PtyEventStartError {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string message */ 1:
message.message = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyEventStartError, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* string message = 1; */
if (message.message !== "")
writer.tag(1, WireType.LengthDelimited).string(message.message);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyEventStartError
*/
export const PtyEventStartError = new PtyEventStartError$Type();
// @generated message type with reflection information, may provide speed optimized methods
class PtyCwd$Type extends MessageType<PtyCwd> {
constructor() {
super("PtyCwd", [
{ no: 1, name: "cwd", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
]);
}
create(value?: PartialMessage<PtyCwd>): PtyCwd {
const message = globalThis.Object.create((this.messagePrototype!));
message.cwd = "";
if (value !== undefined)
reflectionMergePartial<PtyCwd>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PtyCwd): PtyCwd {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string cwd */ 1:
message.cwd = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: PtyCwd, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* string cwd = 1; */
if (message.cwd !== "")
writer.tag(1, WireType.LengthDelimited).string(message.cwd);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message PtyCwd
*/
export const PtyCwd = new PtyCwd$Type();
/**
* @generated ServiceType for protobuf service PtyHost
*/
export const PtyHost = new ServiceType("PtyHost", [
{ name: "CreatePtyProcess", options: {}, I: PtyCreate, O: PtyId },
{ name: "ExchangeEvents", serverStreaming: true, clientStreaming: true, options: {}, I: PtyClientEvent, O: PtyServerEvent },
{ name: "GetCwd", options: {}, I: PtyId, O: PtyCwd }
]);
@@ -17,5 +17,6 @@
*/
export * from './types';
export * from '../api/protogen/ptyHostService_grpc_pb';
export * from '../api/protogen/ptyHostService_pb.grpc-server';
export * from '../api/protogen/ptyHostService_pb.grpc-client';
export * from '../api/protogen/ptyHostService_pb';
@@ -20,6 +20,12 @@ import { ServerDuplexStream } from '@grpc/grpc-js';
import Logger from 'teleterm/logger';
import {
ptyEventOneOfIsData,
ptyEventOneOfIsResize,
ptyEventOneOfIsStart,
} from 'teleterm/helpers';
import {
PtyClientEvent,
PtyEventData,
@@ -43,7 +49,7 @@ export class PtyEventsStreamHandler {
private readonly ptyProcesses: Map<string, PtyProcess>
) {
this.ptyId = stream.metadata.get('ptyId')[0].toString();
this.ptyProcess = ptyProcesses.get(this.ptyId);
this.ptyProcess = ptyProcesses.get(this.ptyId)!;
this.logger = new Logger(`PtyEventsStreamHandler (id: ${this.ptyId})`);
stream.addListener('data', event => this.handleStreamData(event));
@@ -52,49 +58,70 @@ export class PtyEventsStreamHandler {
}
private handleStreamData(event: PtyClientEvent): void {
switch (event.getEventCase()) {
case PtyClientEvent.EventCase.START:
return this.handleStartEvent(event.getStart());
case PtyClientEvent.EventCase.DATA:
return this.handleDataEvent(event.getData());
case PtyClientEvent.EventCase.RESIZE:
return this.handleResizeEvent(event.getResize());
if (ptyEventOneOfIsStart(event.event)) {
return this.handleStartEvent(event.event.start);
}
if (ptyEventOneOfIsData(event.event)) {
return this.handleDataEvent(event.event.data);
}
if (ptyEventOneOfIsResize(event.event)) {
return this.handleResizeEvent(event.event.resize);
}
}
private handleStartEvent(event: PtyEventStart): void {
this.ptyProcess.onData(data =>
this.stream.write(
new PtyServerEvent().setData(new PtyEventData().setMessage(data))
PtyServerEvent.create({
event: {
oneofKind: 'data',
data: PtyEventData.create({ message: data }),
},
})
)
);
this.ptyProcess.onOpen(() =>
this.stream.write(new PtyServerEvent().setOpen(new PtyEventOpen()))
this.stream.write(
PtyServerEvent.create({
event: {
oneofKind: 'open',
open: PtyEventOpen.create(),
},
})
)
);
this.ptyProcess.onExit(({ exitCode, signal }) =>
this.stream.write(
new PtyServerEvent().setExit(
new PtyEventExit().setExitCode(exitCode).setSignal(signal)
)
PtyServerEvent.create({
event: {
oneofKind: 'exit',
exit: PtyEventExit.create({ exitCode, signal }),
},
})
)
);
this.ptyProcess.onStartError(message => {
this.stream.write(
new PtyServerEvent().setStartError(
new PtyEventStartError().setMessage(message)
)
PtyServerEvent.create({
event: {
oneofKind: 'startError',
startError: PtyEventStartError.create({ message }),
},
})
);
});
this.ptyProcess.start(event.getColumns(), event.getRows());
this.ptyProcess.start(event.columns, event.rows);
this.logger.info(`stream has started`);
}
private handleDataEvent(event: PtyEventData): void {
this.ptyProcess.write(event.getMessage());
this.ptyProcess.write(event.message);
}
private handleResizeEvent(event: PtyEventResize): void {
this.ptyProcess.resize(event.getColumns(), event.getRows());
this.ptyProcess.resize(event.columns, event.rows);
}
private handleStreamError(error: Error): void {
@@ -20,26 +20,28 @@ import { unique } from 'teleterm/ui/utils';
import Logger from 'teleterm/logger';
import { Struct } from 'teleterm/sharedProcess/api/protogen/google/protobuf/struct_pb';
import { PtyProcess } from './ptyProcess';
import { IPtyHostServer } from './../api/protogen/ptyHostService_grpc_pb';
import { IPtyHost } from './../api/protogen/ptyHostService_pb.grpc-server';
import { PtyCwd, PtyId } from './../api/protogen/ptyHostService_pb';
import { PtyEventsStreamHandler } from './ptyEventsStreamHandler';
export function createPtyHostService(): IPtyHostServer {
export function createPtyHostService(): IPtyHost {
const logger = new Logger('PtyHostService');
const ptyProcesses = new Map<string, PtyProcess>();
return {
createPtyProcess: (call, callback) => {
const ptyOptions = call.request.toObject();
const ptyOptions = call.request;
const ptyId = unique();
try {
const ptyProcess = new PtyProcess({
path: ptyOptions.path,
args: ptyOptions.argsList,
args: ptyOptions.args,
cwd: ptyOptions.cwd,
ptyId,
env: call.request.getEnv()?.toJavaScript() as Record<string, string>,
env: Struct.toJson(call.request.env!) as Record<string, string>,
initMessage: ptyOptions.initMessage,
});
ptyProcesses.set(ptyId, ptyProcess);
@@ -48,11 +50,11 @@ export function createPtyHostService(): IPtyHostServer {
callback(error);
return;
}
callback(null, new PtyId().setId(ptyId));
callback(null, PtyId.create({ id: ptyId }));
logger.info(`created PTY process for id ${ptyId}`);
},
getCwd: (call, callback) => {
const id = call.request.getId();
const id = call.request.id;
const ptyProcess = ptyProcesses.get(id);
if (!ptyProcess) {
const message = `PTY process with id: ${id} does not exist`;
@@ -62,7 +64,7 @@ export function createPtyHostService(): IPtyHostServer {
ptyProcess
.getCwd()
.then(cwd => {
const response = new PtyCwd().setCwd(cwd);
const response = PtyCwd.create({ cwd });
callback(null, response);
})
.catch(error => {
@@ -31,7 +31,8 @@ import {
import { RuntimeSettings } from 'teleterm/mainProcess/types';
import Logger from 'teleterm/logger';
import { PtyHostService } from './api/protogen/ptyHostService_grpc_pb';
import { ptyHostDefinition } from 'teleterm/sharedProcess/api/protogen/ptyHostService_pb.grpc-server';
import { createPtyHostService } from './ptyHost/ptyHostService';
const runtimeSettings = getRuntimeSettings();
@@ -73,8 +74,7 @@ async function initializeServer(
}
const server = new Server();
// @ts-expect-error we have a typed service
server.addService(PtyHostService, createPtyHostService());
server.addService(ptyHostDefinition, createPtyHostService());
// grpc-js requires us to pass localhost:port for TCP connections,
const grpcServerAddress = address.replace('tcp://', '');
+18 -14
View File
@@ -16,13 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { ITshdEventsServiceServer } from 'gen-proto-js/teleport/lib/teleterm/v1/tshd_events_service_grpc_pb';
import { ITshdEventsService } from 'gen-proto-ts/teleport/lib/teleterm/v1/tshd_events_service_pb.grpc-server';
import { sendUnaryData, ServerUnaryCall } from 'grpc';
import { TshdClient } from 'teleterm/services/tshd/types';
import { PtyServiceClient } from 'teleterm/services/pty';
import { RuntimeSettings, MainProcessClient } from 'teleterm/mainProcess/types';
import { FileStorage } from 'teleterm/services/fileStorage';
import { Logger, LoggerService } from 'teleterm/services/logger/types';
import { FileStorage } from 'teleterm/services/fileStorage';
import { MainProcessClient, RuntimeSettings } from 'teleterm/mainProcess/types';
import { PtyServiceClient } from 'teleterm/services/pty';
import { TshdClient } from 'teleterm/services/tshd/types';
export type {
Logger,
@@ -78,13 +80,11 @@ export type {
* actually uses the returned object.
*/
export type TshdEventContextBridgeService = {
[RpcName in keyof ITshdEventsServiceServer]: (args: {
[RpcName in keyof ITshdEventsService]: (args: {
/**
* request is the result of calling call.request.toObject() in a gRPC handler.
*/
request: ReturnType<
Parameters<ITshdEventsServiceServer[RpcName]>[0]['request']['toObject']
>;
request: ExtractRequestType<Parameters<ITshdEventsService[RpcName]>[0]>;
/**
* onRequestCancelled sets up a callback that is called when the request gets canceled by the
* client (tshd in this case).
@@ -93,14 +93,18 @@ export type TshdEventContextBridgeService = {
}) => Promise<
// The following type maps to the object version of the response type expected as the second
// argument to the callback function in a gRPC handler.
ReturnType<
Parameters<
Parameters<ITshdEventsServiceServer[RpcName]>[1]
>[1]['toObject']
>
ExtractResponseType<Parameters<ITshdEventsService[RpcName]>[1]>
>;
};
export type ExtractRequestType<T> = T extends ServerUnaryCall<infer Req, any>
? Req
: never;
export type ExtractResponseType<T> = T extends sendUnaryData<infer Res>
? Res
: never;
export type ElectronGlobals = {
readonly mainProcessClient: MainProcessClient;
readonly tshClient: TshdClient;
@@ -36,11 +36,8 @@ export const AppInitializer = () => {
const initializeApp = useCallback(async () => {
try {
await appContext.pullInitialState();
setShouldShowUi(true);
await showStartupModalsAndNotifications(appContext);
appContext.mainProcessClient.signalUserInterfaceReadiness({
success: true,
});
@@ -50,7 +50,7 @@ function makeProps(): ClusterLoginPresentationProps {
data: {
preferredMfa: 'webauthn',
localAuthEnabled: true,
authProvidersList: [],
authProviders: [],
type: '',
secondFactor: 'optional',
hasMessageOfTheDay: false,
@@ -172,7 +172,7 @@ export const SsoOnly = () => {
const props = makeProps();
props.initAttempt.data.localAuthEnabled = false;
props.initAttempt.data.authType = 'github';
props.initAttempt.data.authProvidersList = [
props.initAttempt.data.authProviders = [
{ type: 'github', name: 'github', displayName: 'github' },
{ type: 'saml', name: 'microsoft', displayName: 'microsoft' },
];
@@ -205,7 +205,7 @@ export const LocalLoggedInUserWithPasswordless = () => {
export const LocalWithSso = () => {
const props = makeProps();
props.initAttempt.data.authProvidersList = [
props.initAttempt.data.authProviders = [
{ type: 'github', name: 'github', displayName: 'github' },
{ type: 'saml', name: 'microsoft', displayName: 'microsoft' },
];
@@ -243,7 +243,7 @@ export const PasswordlessWithLocalLoggedInUser = () => {
export const SsoWithLocalAndPasswordless = () => {
const props = makeProps();
props.initAttempt.data.authType = 'github';
props.initAttempt.data.authProvidersList = [
props.initAttempt.data.authProviders = [
{ type: 'github', name: 'github', displayName: 'github' },
{ type: 'saml', name: 'microsoft', displayName: 'microsoft' },
];
@@ -39,7 +39,7 @@ export default function LoginForm(props: Props) {
const {
loginAttempt,
onAbort,
authProvidersList,
authProviders,
localAuthEnabled = true,
shouldPromptSsoStatus,
webauthnLogin,
@@ -53,7 +53,7 @@ export default function LoginForm(props: Props) {
return <PromptSsoStatus onCancel={onAbort} />;
}
const ssoEnabled = authProvidersList?.length > 0;
const ssoEnabled = authProviders?.length > 0;
// If local auth was not enabled, disregard any primary auth type config
// and display sso providers if any.
@@ -107,7 +107,7 @@ const Primary = ({
hasTransitionEnded,
...otherProps
}: Props & StepComponentProps) => {
const ssoEnabled = otherProps.authProvidersList?.length > 0;
const ssoEnabled = otherProps.authProviders?.length > 0;
let otherOptionsAvailable = true;
let $primary;
@@ -161,7 +161,7 @@ const Secondary = ({
refCallback,
...otherProps
}: Props & StepComponentProps) => {
const ssoEnabled = otherProps.authProvidersList?.length > 0;
const ssoEnabled = otherProps.authProviders?.length > 0;
const { primaryAuthType, allowPasswordless } = otherProps;
let $secondary;
@@ -25,7 +25,7 @@ import type { Props } from '../FormLogin';
export const FormSso = ({
loginAttempt,
authProvidersList,
authProviders,
onLoginWithSso,
autoFocus = false,
}: Props) => {
@@ -34,7 +34,7 @@ export const FormSso = ({
<SSOButtonList
prefixText="Login with"
isDisabled={loginAttempt.status === 'processing'}
providers={authProvidersList}
providers={authProviders}
onClick={onLoginWithSso}
autoFocus={autoFocus}
/>
@@ -136,7 +136,7 @@ export function NoAccess() {
export function AccessUnknown() {
const cluster = makeRootCluster();
cluster.loggedInUser.userType = UserType.USER_TYPE_UNSPECIFIED;
cluster.loggedInUser.userType = UserType.UNSPECIFIED;
const appContext = new MockAppContext({});
return (
@@ -167,7 +167,7 @@ function setupAppContext(): {
list: true,
read: true,
edit: true,
pb_delete: true,
delete: true,
use: true,
},
},
@@ -68,7 +68,7 @@ export function Running() {
Promise.resolve(
makeServer({
hostname: 'staging-mac-mini',
labelsList: makeLabelsList({
labels: makeLabelsList({
hostname: 'staging-mac-mini',
'teleport.dev/connect-my-computer/owner': '[email protected]',
}),
@@ -227,7 +227,7 @@ export function Status(props: { closeDocument?: () => void }) {
<LabelsContainer gap={1} className={state}>
{/* Explicitly check for existence of agentNode because Transition doesn't seem to
unmount immediately when `in` becomes falsy. */}
{agentNode?.labelsList && renderLabels(agentNode.labelsList)}
{agentNode?.labels && renderLabels(agentNode.labels)}
</LabelsContainer>
)}
</Transition>
@@ -70,7 +70,7 @@ test.each(testCases)('$name', testCase => {
list: false,
use: false,
read: false,
pb_delete: false,
delete: false,
},
},
});
@@ -52,13 +52,13 @@ export function getConnectMyComputerAccess(
if (
!loggedInUser ||
loggedInUser.userType === tsh.UserType.USER_TYPE_UNSPECIFIED ||
loggedInUser.userType === tsh.UserType.UNSPECIFIED ||
!loggedInUser.acl
) {
return { status: 'unknown' };
}
if (loggedInUser.userType === tsh.UserType.USER_TYPE_SSO) {
if (loggedInUser.userType === tsh.UserType.SSO) {
return { status: 'no-access', reason: 'sso-user' };
}
@@ -58,7 +58,7 @@ function getMocks() {
list: false,
use: false,
read: false,
pb_delete: false,
delete: false,
},
},
}),
@@ -114,7 +114,7 @@ function HttpApp() {
function Server() {
const appContext = new MockAppContext();
const testCluster = makeRootCluster();
testCluster.loggedInUser.sshLoginsList = ['ec2-user'];
testCluster.loggedInUser.sshLogins = ['ec2-user'];
appContext.workspacesService.setState(d => {
d.rootClusterUri = testCluster.uri;
});
@@ -50,7 +50,7 @@ export function ConnectServerActionButton(props: {
function getSshLogins(): string[] {
const cluster = ctx.clustersService.findClusterByResource(props.server.uri);
return cluster?.loggedInUser?.sshLoginsList || [];
return cluster?.loggedInUser?.sshLogins || [];
}
function connect(login: string): void {
@@ -65,13 +65,13 @@ export const OnlineEmptyResourcesAndCanAddResourcesAndConnectComputer = () => {
makeRootCluster({
uri: rootClusterDoc.clusterUri,
loggedInUser: makeLoggedInUser({
userType: tsh.UserType.USER_TYPE_LOCAL,
userType: tsh.UserType.LOCAL,
acl: {
tokens: {
create: true,
list: true,
edit: true,
pb_delete: true,
delete: true,
read: true,
use: true,
},
@@ -101,13 +101,13 @@ export const OnlineEmptyResourcesAndCanAddResourcesButCannotConnectComputer =
makeRootCluster({
uri: rootClusterDoc.clusterUri,
loggedInUser: makeLoggedInUser({
userType: tsh.UserType.USER_TYPE_SSO,
userType: tsh.UserType.SSO,
acl: {
tokens: {
create: true,
list: true,
edit: true,
pb_delete: true,
delete: true,
read: true,
use: true,
},
@@ -141,7 +141,7 @@ export const OnlineEmptyResourcesAndCannotAddResources = () => {
create: false,
list: true,
edit: true,
pb_delete: true,
delete: true,
read: true,
use: true,
},
@@ -48,13 +48,13 @@ it('displays a button for Connect My Computer in the empty state if the user can
makeRootCluster({
uri: doc.clusterUri,
loggedInUser: makeLoggedInUser({
userType: tsh.UserType.USER_TYPE_LOCAL,
userType: tsh.UserType.LOCAL,
acl: {
tokens: {
create: true,
list: true,
edit: true,
pb_delete: true,
delete: true,
read: true,
use: true,
},
@@ -118,13 +118,13 @@ it('does not display a button for Connect My Computer in the empty state if the
makeRootCluster({
uri: doc.clusterUri,
loggedInUser: makeLoggedInUser({
userType: tsh.UserType.USER_TYPE_LOCAL,
userType: tsh.UserType.LOCAL,
acl: {
tokens: {
create: false,
list: true,
edit: true,
pb_delete: true,
delete: true,
read: true,
use: true,
},
@@ -37,10 +37,10 @@ import * as icons from 'design/Icon';
import Image from 'design/Image';
import stack from 'design/assets/resources/stack.png';
import { DefaultTab } from 'shared/services/unifiedResourcePreferences';
import { Attempt } from 'shared/hooks/useAsync';
import { DefaultTab } from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb';
import {
UnifiedResourceResponse,
UserPreferences,
@@ -86,8 +86,7 @@ export function UnifiedResources(props: {
() => ({
kinds: props.queryParams.resourceKinds,
sort: props.queryParams.sort,
pinnedOnly:
unifiedResourcePreferences.defaultTab === DefaultTab.DEFAULT_TAB_PINNED,
pinnedOnly: unifiedResourcePreferences.defaultTab === DefaultTab.PINNED,
search: props.queryParams.advancedSearchEnabled
? ''
: props.queryParams.search,
@@ -190,7 +189,7 @@ const Resources = memo(
field: props.queryParams.sort.fieldName,
},
search: props.queryParams.search,
kindsList: props.queryParams.kinds,
kinds: props.queryParams.kinds,
query: props.queryParams.query,
pinnedOnly: props.queryParams.pinnedOnly,
startKey: paginationParams.startKey,
@@ -228,24 +227,23 @@ const Resources = memo(
return cleanup;
}, [onResourcesRefreshRequest, fetch, clear]);
const resourceIdsList =
props.userPreferences.clusterPreferences?.pinnedResources
?.resourceIdsList;
const resourceIds =
props.userPreferences.clusterPreferences?.pinnedResources?.resourceIds;
const { updateUserPreferences } = props;
const pinning = useMemo<UnifiedResourcesPinning>(() => {
return resourceIdsList
return resourceIds
? {
kind: 'supported',
getClusterPinnedResources: async () => resourceIdsList,
getClusterPinnedResources: async () => resourceIds,
updateClusterPinnedResources: pinnedIds =>
updateUserPreferences({
clusterPreferences: {
pinnedResources: { resourceIdsList: pinnedIds },
pinnedResources: { resourceIds: pinnedIds },
},
}),
}
: { kind: 'not-supported' };
}, [updateUserPreferences, resourceIdsList]);
}, [updateUserPreferences, resourceIds]);
return (
<SharedUnifiedResources
@@ -302,7 +300,7 @@ const mapToSharedResource = (
return {
resource: {
kind: 'node' as const,
labels: server.labelsList,
labels: server.labels,
id: server.name,
hostname: server.hostname,
addr: server.addr,
@@ -319,7 +317,7 @@ const mapToSharedResource = (
return {
resource: {
kind: 'db' as const,
labels: database.labelsList,
labels: database.labels,
description: database.desc,
name: database.name,
type: formatDatabaseInfo(
@@ -339,7 +337,7 @@ const mapToSharedResource = (
return {
resource: {
kind: 'kube_cluster' as const,
labels: kube.labelsList,
labels: kube.labels,
name: kube.name,
},
ui: {
@@ -353,7 +351,7 @@ const mapToSharedResource = (
return {
resource: {
kind: 'app' as const,
labels: app.labelsList,
labels: app.labels,
name: app.name,
id: app.name,
addrWithProtocol: app.addrWithProtocol,
@@ -17,11 +17,12 @@
*/
import { renderHook, act } from '@testing-library/react';
import {
ViewMode,
DefaultTab,
LabelsViewMode,
} from 'shared/services/unifiedResourcePreferences';
ViewMode,
} from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb';
import { makeRootCluster } from 'teleterm/services/tshd/testHelpers';
import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider';
@@ -33,11 +34,11 @@ import { useUserPreferences } from './useUserPreferences';
const cluster = makeRootCluster();
const preferences: UserPreferences = {
clusterPreferences: { pinnedResources: { resourceIdsList: ['abc'] } },
clusterPreferences: { pinnedResources: { resourceIds: ['abc'] } },
unifiedResourcePreferences: {
viewMode: ViewMode.VIEW_MODE_CARD,
defaultTab: DefaultTab.DEFAULT_TAB_ALL,
labelsViewMode: LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED,
viewMode: ViewMode.CARD,
defaultTab: DefaultTab.ALL,
labelsViewMode: LabelsViewMode.COLLAPSED,
},
};
@@ -140,9 +141,9 @@ describe('updating preferences', () => {
const newPreferences: UserPreferences = {
clusterPreferences: {},
unifiedResourcePreferences: {
viewMode: ViewMode.VIEW_MODE_LIST,
defaultTab: DefaultTab.DEFAULT_TAB_PINNED,
labelsViewMode: LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED,
viewMode: ViewMode.LIST,
defaultTab: DefaultTab.PINNED,
labelsViewMode: LabelsViewMode.COLLAPSED,
},
};
@@ -201,9 +202,9 @@ describe('updating preferences', () => {
const newPreferences: UserPreferences = {
clusterPreferences: {},
unifiedResourcePreferences: {
viewMode: ViewMode.VIEW_MODE_LIST,
defaultTab: DefaultTab.DEFAULT_TAB_PINNED,
labelsViewMode: LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED,
viewMode: ViewMode.LIST,
defaultTab: DefaultTab.PINNED,
labelsViewMode: LabelsViewMode.COLLAPSED,
},
};
@@ -232,11 +233,11 @@ describe('updating preferences', () => {
// (e.g., because they were changed it in the browser in the meantime)
act(() =>
resolveUpdateUserPreferencesPromise({
clusterPreferences: { pinnedResources: { resourceIdsList: ['abc'] } },
clusterPreferences: { pinnedResources: { resourceIds: ['abc'] } },
unifiedResourcePreferences: {
viewMode: ViewMode.VIEW_MODE_CARD,
defaultTab: DefaultTab.DEFAULT_TAB_PINNED,
labelsViewMode: LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED,
viewMode: ViewMode.CARD,
defaultTab: DefaultTab.PINNED,
labelsViewMode: LabelsViewMode.COLLAPSED,
},
})
);
@@ -248,7 +249,7 @@ describe('updating preferences', () => {
);
expect(
result.current.userPreferences.clusterPreferences.pinnedResources
.resourceIdsList
.resourceIds
).toEqual(['abc']);
});
});
@@ -32,10 +32,10 @@ import {
import {
DefaultTab,
ViewMode,
UnifiedResourcePreferences,
LabelsViewMode,
} from 'shared/services/unifiedResourcePreferences';
UnifiedResourcePreferences,
ViewMode,
} from 'gen-proto-ts/teleport/userpreferences/v1/unified_resource_preferences_pb';
import { useAppContext } from 'teleterm/ui/appContextProvider';
@@ -62,16 +62,16 @@ export function useUserPreferences(clusterUri: ClusterUri): {
routing.ensureRootClusterUri(clusterUri)
)
) || {
defaultTab: DefaultTab.DEFAULT_TAB_ALL,
viewMode: ViewMode.VIEW_MODE_CARD,
labelsViewMode: LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED,
defaultTab: DefaultTab.ALL,
viewMode: ViewMode.CARD,
labelsViewMode: LabelsViewMode.COLLAPSED,
}
);
const [clusterPreferences, setClusterPreferences] = useState<
UserPreferences['clusterPreferences']
>({
// we pass an empty array, so pinning is enabled by default
pinnedResources: { resourceIdsList: [] },
pinnedResources: { resourceIds: [] },
});
const [initialFetchAttempt, runInitialFetchAttempt] = useAsync(
@@ -221,17 +221,16 @@ function mergeWithDefaultUnifiedResourcePreferences(
return {
defaultTab: unifiedResourcePreferences
? unifiedResourcePreferences.defaultTab
: DefaultTab.DEFAULT_TAB_ALL,
: DefaultTab.ALL,
viewMode:
unifiedResourcePreferences &&
unifiedResourcePreferences.viewMode !== ViewMode.VIEW_MODE_UNSPECIFIED
unifiedResourcePreferences.viewMode !== ViewMode.UNSPECIFIED
? unifiedResourcePreferences.viewMode
: ViewMode.VIEW_MODE_CARD,
: ViewMode.CARD,
labelsViewMode:
unifiedResourcePreferences &&
unifiedResourcePreferences.labelsViewMode !==
LabelsViewMode.LABELS_VIEW_MODE_UNSPECIFIED
unifiedResourcePreferences.labelsViewMode !== LabelsViewMode.UNSPECIFIED
? unifiedResourcePreferences.labelsViewMode
: LabelsViewMode.LABELS_VIEW_MODE_COLLAPSED,
: LabelsViewMode.COLLAPSED,
};
}
@@ -21,7 +21,8 @@ import {
createFileTransferEventsEmitter,
} from 'shared/components/FileTransfer';
import { FileTransferDirection } from 'teleterm/services/tshd/types';
import { FileTransferDirection } from 'gen-proto-ts/teleport/lib/teleterm/v1/service_pb';
import { retryWithRelogin } from 'teleterm/ui/utils';
import { useAppContext } from 'teleterm/ui/appContextProvider';
import { IAppContext } from 'teleterm/ui/types';
@@ -40,7 +41,7 @@ export function useTshFileTransferHandlers() {
appContext,
file,
abortController,
FileTransferDirection.FILE_TRANSFER_DIRECTION_UPLOAD
FileTransferDirection.UPLOAD
);
},
download(
@@ -51,7 +52,7 @@ export function useTshFileTransferHandlers() {
appContext,
file,
abortController,
FileTransferDirection.FILE_TRANSFER_DIRECTION_DOWNLOAD
FileTransferDirection.DOWNLOAD
);
},
};
@@ -20,11 +20,11 @@ import React, { useRef, useEffect } from 'react';
import { useAsync } from 'shared/hooks/useAsync';
import { HeadlessAuthenticationState } from 'gen-proto-ts/teleport/lib/teleterm/v1/service_pb';
import { useAppContext } from 'teleterm/ui/appContextProvider';
import { RootClusterUri } from 'teleterm/ui/uri';
import { HeadlessAuthenticationState } from 'teleterm/services/tshd/types';
import { HeadlessPrompt } from './HeadlessPrompt';
interface HeadlessAuthenticationProps {
@@ -55,7 +55,7 @@ export function HeadlessAuthentication(props: HeadlessAuthenticationProps) {
async function handleHeadlessApprove(): Promise<void> {
const [, error] = await updateHeadlessState(
HeadlessAuthenticationState.HEADLESS_AUTHENTICATION_STATE_APPROVED
HeadlessAuthenticationState.APPROVED
);
if (!error) {
props.onSuccess();
@@ -64,7 +64,7 @@ export function HeadlessAuthentication(props: HeadlessAuthenticationProps) {
async function handleHeadlessReject(): Promise<void> {
const [, error] = await updateHeadlessState(
HeadlessAuthenticationState.HEADLESS_AUTHENTICATION_STATE_DENIED
HeadlessAuthenticationState.DENIED
);
if (!error) {
props.onSuccess();
@@ -65,7 +65,7 @@ export function mapToAction(
parameter: {
getSuggestions: async () =>
ctx.clustersService.findClusterByResource(result.resource.uri)
?.loggedInUser?.sshLoginsList,
?.loggedInUser?.sshLogins,
placeholder: 'Provide login',
},
perform: login => {
@@ -966,7 +966,7 @@ function Labels(
scoreMap.set(match.labelName, currentScore + match.score);
});
const sortedLabelsList = [...searchResult.resource.labelsList];
const sortedLabelsList = [...searchResult.resource.labels];
sortedLabelsList.sort(
(a, b) =>
// Highest score first.
@@ -110,7 +110,7 @@ const SearchResultItems = () => {
hostname: 'long-label-list',
uri: `${clusterUri}/servers/2f96e498-88ec-442f-a25b-569fa915041c`,
name: '2f96e498-88ec-442f-a25b-569fa915041c',
labelsList: makeLabelsList({
labels: makeLabelsList({
arch: 'aarch64',
external: '32.192.113.93',
internal: '10.0.0.175',
@@ -127,7 +127,7 @@ const SearchResultItems = () => {
tunnel: true,
uri: `${clusterUri}/servers/90a29595-aac7-42eb-a484-c6c0e23f1a21`,
name: '90a29595-aac7-42eb-a484-c6c0e23f1a21',
labelsList: makeLabelsList({
labels: makeLabelsList({
arch: 'aarch64',
service: 'ansible',
external: '32.192.113.93',
@@ -144,7 +144,7 @@ const SearchResultItems = () => {
tunnel: true,
uri: `${clusterUri}/servers/bbaaceba-6bd1-4750-9d3d-1a80e0cc8a63`,
name: 'bbaaceba-6bd1-4750-9d3d-1a80e0cc8a63',
labelsList: makeLabelsList({
labels: makeLabelsList({
internal: '10.0.0.175',
service: 'ansible',
external: '32.192.113.93',
@@ -158,7 +158,7 @@ const SearchResultItems = () => {
hostname:
'super-long-server-name-with-uuid-2f96e498-88ec-442f-a25b-569fa915041c',
uri: `${longClusterUri}/servers/super-long-desc`,
labelsList: makeLabelsList({
labels: makeLabelsList({
internal: '10.0.0.175',
service: 'ansible',
external: '32.192.113.93',
@@ -172,7 +172,7 @@ const SearchResultItems = () => {
uri: `${clusterUri}/apps/no-desc`,
name: 'no-desc',
desc: '',
labelsList: makeLabelsList({
labels: makeLabelsList({
access: 'cloudwatch-metrics,ec2,s3,cloudtrail',
'aws/Environment': 'demo-13-biz',
'aws/Owner': 'foobar',
@@ -187,7 +187,7 @@ const SearchResultItems = () => {
uri: `${clusterUri}/apps/short-desc`,
name: 'short-desc',
desc: 'Lorem ipsum',
labelsList: makeLabelsList({
labels: makeLabelsList({
access: 'cloudwatch-metrics,ec2,s3,cloudtrail',
'aws/Environment': 'demo-13-biz',
'aws/Owner': 'foobar',
@@ -202,7 +202,7 @@ const SearchResultItems = () => {
uri: `${clusterUri}/apps/long-desc`,
name: 'long-desc',
desc: 'Eget dignissim lectus nisi vitae nunc',
labelsList: makeLabelsList({
labels: makeLabelsList({
access: 'cloudwatch-metrics,ec2,s3,cloudtrail',
'aws/Environment': 'demo-13-biz',
'aws/Owner': 'foobar',
@@ -217,7 +217,7 @@ const SearchResultItems = () => {
uri: `${clusterUri}/apps/super-long-desc`,
name: 'super-long-desc',
desc: 'Duis id tortor at purus tincidunt finibus. Mauris eu semper orci, non commodo lacus. Praesent sollicitudin magna id laoreet porta. Nunc lobortis varius sem vel fringilla.',
labelsList: makeLabelsList({
labels: makeLabelsList({
access: 'cloudwatch-metrics,ec2,s3,cloudtrail',
'aws/Environment': 'demo-13-biz',
'aws/Owner': 'foobar',
@@ -232,7 +232,7 @@ const SearchResultItems = () => {
name: 'super-long-app-with-uuid-1f96e498-88ec-442f-a25b-569fa915041c',
desc: 'short-desc',
uri: `${longClusterUri}/apps/super-long-desc`,
labelsList: makeLabelsList({
labels: makeLabelsList({
access: 'cloudwatch-metrics,ec2,s3,cloudtrail',
'aws/Environment': 'demo-13-biz',
'aws/Owner': 'foobar',
@@ -248,7 +248,7 @@ const SearchResultItems = () => {
uri: `${clusterUri}/dbs/no-desc`,
name: 'no-desc',
desc: '',
labelsList: makeLabelsList({
labels: makeLabelsList({
'aws/Accounting': 'dev-ops',
'aws/Environment': 'demo-13-biz',
'aws/Name': 'db-bastion-4-13biz',
@@ -266,7 +266,7 @@ const SearchResultItems = () => {
uri: `${clusterUri}/dbs/short-desc`,
name: 'short-desc',
desc: 'Lorem ipsum',
labelsList: makeLabelsList({
labels: makeLabelsList({
'aws/Environment': 'demo-13-biz',
'aws/Name': 'db-bastion-4-13biz',
'aws/Accounting': 'dev-ops',
@@ -284,7 +284,7 @@ const SearchResultItems = () => {
uri: `${clusterUri}/dbs/long-desc`,
name: 'long-desc',
desc: 'Eget dignissim lectus nisi vitae nunc',
labelsList: makeLabelsList({
labels: makeLabelsList({
'aws/Environment': 'demo-13-biz',
'aws/Name': 'db-bastion-4-13biz',
'aws/Accounting': 'dev-ops',
@@ -302,7 +302,7 @@ const SearchResultItems = () => {
uri: `${clusterUri}/dbs/super-long-desc`,
name: 'super-long-desc',
desc: 'Duis id tortor at purus tincidunt finibus. Mauris eu semper orci, non commodo lacus. Praesent sollicitudin magna id laoreet porta. Nunc lobortis varius sem vel fringilla.',
labelsList: makeLabelsList({
labels: makeLabelsList({
'aws/Environment': 'demo-13-biz',
'aws/Accounting': 'dev-ops',
'aws/Name': 'db-bastion-4-13biz',
@@ -319,7 +319,7 @@ const SearchResultItems = () => {
resource: makeDatabase({
name: 'super-long-server-db-with-uuid-2f96e498-88ec-442f-a25b-569fa915041c',
uri: `${longClusterUri}/dbs/super-long-desc`,
labelsList: makeLabelsList({
labels: makeLabelsList({
'aws/Environment': 'demo-13-biz',
'aws/Accounting': 'dev-ops',
'aws/Name': 'db-bastion-4-13biz',
@@ -335,7 +335,7 @@ const SearchResultItems = () => {
kind: 'kube',
resource: makeKube({
name: 'short-label-list',
labelsList: makeLabelsList({
labels: makeLabelsList({
'im-just-a-smol': 'kube',
kube: 'kubersson',
with: 'little-to-no-labels',
@@ -347,7 +347,7 @@ const SearchResultItems = () => {
resource: makeKube({
name: 'long-label-list',
uri: `${clusterUri}/kubes/long-label-list`,
labelsList: makeLabelsList({
labels: makeLabelsList({
'aws/Environment': 'demo-13-biz',
'aws/Owner': 'foobar',
'aws/Name': 'db-bastion-4-13biz',
@@ -361,7 +361,7 @@ const SearchResultItems = () => {
resource: makeKube({
name: 'super-long-kube-name-with-uuid-2f96e498-88ec-442f-a25b-569fa915041c',
uri: `/clusters/teleport-very-long-cluster-name-with-uuid-2f96e498-88ec-442f-a25b-569fa915041c/kubes/super-long-desc`,
labelsList: makeLabelsList({
labels: makeLabelsList({
'im-just-a-smol': 'kube',
kube: 'kubersson',
with: 'little-to-no-labels',
@@ -109,10 +109,7 @@ export const mainResourceField: {
// fields of string type.
export const searchableFields: {
[Kind in ResourceSearchResult['kind']]: ReadonlyArray<
Exclude<
keyof resourcesServiceTypes.SearchResultResource<Kind>,
'labelsList'
>
Exclude<keyof resourcesServiceTypes.SearchResultResource<Kind>, 'labels'>
>;
} = {
server: ['name', 'hostname', 'addr'],
@@ -58,7 +58,7 @@ describe('rankResults', () => {
const server = makeResourceResult({
kind: 'server',
resource: makeServer({
labelsList: makeLabelsList({ quux: 'bar-baz', foo: 'bar' }),
labels: makeLabelsList({ quux: 'bar-baz', foo: 'bar' }),
}),
});
@@ -87,7 +87,7 @@ describe('rankResults', () => {
makeResourceResult({
kind: 'server',
resource: makeServer({
labelsList: makeLabelsList({ foo: 'bar1' }),
labels: makeLabelsList({ foo: 'bar1' }),
}),
})
);
@@ -99,7 +99,7 @@ describe('rankResults', () => {
kind: 'server',
resource: makeServer({
uri: lowestScoreServerUri,
labelsList: makeLabelsList({ foo: 'bar123456' }),
labels: makeLabelsList({ foo: 'bar123456' }),
}),
})
);
@@ -111,7 +111,7 @@ describe('rankResults', () => {
kind: 'server',
resource: makeServer({
uri: highestScoreServerUri,
labelsList: makeLabelsList({ foo: 'bar' }),
labels: makeLabelsList({ foo: 'bar' }),
}),
})
);
@@ -277,7 +277,7 @@ function populateMatches(
const resourceMatches: ResourceMatch<ResourceSearchResult['kind']>[] = [];
terms.forEach(term => {
searchResult.resource.labelsList.forEach(label => {
searchResult.resource.labels.forEach(label => {
// indexOf is faster on Chrome than includes or regex.
// https://jsbench.me/b7lf9kvrux/1
const nameIndex = label.name.toLocaleLowerCase().indexOf(term);
@@ -328,7 +328,7 @@ function calculateScore(
let searchResultScore = 0;
const labelMatches = searchResult.labelMatches.map(match => {
const label = searchResult.resource.labelsList.find(
const label = searchResult.resource.labels.find(
label => label.name === match.labelName
);
let matchedValue: string;
@@ -97,8 +97,8 @@ export function OneClusterWithActiveCluster() {
proxyHost: 'localhost:3080',
loggedInUser: makeLoggedInUser({
name: 'alice',
rolesList: ['access', 'editor'],
sshLoginsList: ['root'],
roles: ['access', 'editor'],
sshLogins: ['root'],
}),
});
@@ -183,8 +183,8 @@ export function ManyClustersWithActiveCluster() {
proxyHost: 'localhost:3080',
loggedInUser: makeLoggedInUser({
name: activeIdentityRootCluster.userName,
rolesList: ['access', 'editor'],
sshLoginsList: ['root'],
roles: ['access', 'editor'],
sshLogins: ['root'],
}),
});
@@ -234,7 +234,7 @@ export function LongNamesWithManyRoles() {
proxyHost: 'localhost:3080',
loggedInUser: makeLoggedInUser({
name: activeIdentityRootCluster.userName,
rolesList: [
roles: [
'circle-mark-app-access',
'grafana-lite-app-access',
'grafana-gold-app-access',
@@ -245,7 +245,7 @@ export function LongNamesWithManyRoles() {
'sales-center-shop-app-access',
'sales-center-floor-db-access',
],
sshLoginsList: ['root'],
sshLogins: ['root'],
}),
});
@@ -48,7 +48,7 @@ export function IdentityList(props: IdentityListProps) {
<Box>
<Text bold>{props.loggedInUser.name}</Text>
<Flex flexWrap="wrap" gap={1}>
{props.loggedInUser.rolesList.map(role => (
{props.loggedInUser.roles.map(role => (
<Label key={role} kind="secondary">
{role}
</Label>
@@ -177,6 +177,7 @@ export class ClustersService extends ImmutableStore<types.ClustersServiceState>
clusters.map(c => [c.uri, this.removeInternalLoginsFromCluster(c)])
);
});
clusters
.filter(c => c.connected)
.forEach(c => this.syncRootClusterAndCatchErrors(c.uri));
@@ -520,7 +521,7 @@ export class ClustersService extends ImmutableStore<types.ClustersServiceState>
// TODO: this information should eventually be gathered by getCluster
const assumedRequests = cluster.loggedInUser
? await this.fetchClusterAssumedRequests(
cluster.loggedInUser.activeRequestsList,
cluster.loggedInUser.activeRequests,
clusterUri
)
: undefined;
@@ -555,7 +556,7 @@ export class ClustersService extends ImmutableStore<types.ClustersServiceState>
requestsMap[request.id] = {
id: request.id,
expires: new Date(request.expires.seconds * 1000),
roles: request.rolesList,
roles: request.roles,
};
return requestsMap;
}, {});
@@ -569,7 +570,7 @@ export class ClustersService extends ImmutableStore<types.ClustersServiceState>
...cluster,
loggedInUser: cluster.loggedInUser && {
...cluster.loggedInUser,
sshLoginsList: cluster.loggedInUser.sshLoginsList.filter(
sshLogins: cluster.loggedInUser.sshLogins.filter(
login => !login.startsWith('-')
),
},
@@ -583,7 +584,7 @@ export function makeServer(source: tsh.Server) {
id: source.name,
clusterId: source.name,
hostname: source.hostname,
labels: source.labelsList,
labels: source.labels,
addr: source.addr,
tunnel: source.tunnel,
sshLogins: [],
@@ -600,7 +601,7 @@ export function makeDatabase(source: tsh.Database) {
source.protocol as DbProtocol
).title,
protocol: source.protocol,
labels: source.labelsList,
labels: source.labels,
};
}
@@ -608,7 +609,7 @@ export function makeKube(source: tsh.Kube) {
return {
uri: source.uri,
name: source.name,
labels: source.labelsList,
labels: source.labels,
};
}
@@ -18,11 +18,9 @@
import { FileTransferListeners } from 'shared/components/FileTransfer';
import {
FileTransferDirection,
FileTransferRequest,
TshdClient,
} from 'teleterm/services/tshd/types';
import { FileTransferDirection } from 'gen-proto-ts/teleport/lib/teleterm/v1/service_pb';
import { FileTransferRequest, TshdClient } from 'teleterm/services/tshd/types';
import { UsageService } from 'teleterm/ui/services/usage';
export class FileTransferService {
@@ -52,17 +50,12 @@ export class FileTransferService {
{ once: true }
);
const listeners = this.tshClient.transferFile(options, abortSignal);
if (
options.direction ===
FileTransferDirection.FILE_TRANSFER_DIRECTION_DOWNLOAD
) {
if (options.direction === FileTransferDirection.DOWNLOAD) {
this.usageService.captureFileTransferRun(options.serverUri, {
isUpload: false,
});
}
if (
options.direction === FileTransferDirection.FILE_TRANSFER_DIRECTION_UPLOAD
) {
if (options.direction === FileTransferDirection.UPLOAD) {
this.usageService.captureFileTransferRun(options.serverUri, {
isUpload: true,
});
@@ -45,7 +45,7 @@ describe('getServerByHostname', () => {
{
name: 'returns a server when the hostname matches a single server',
getServersMockedValue: {
agentsList: [server],
agents: [server],
totalCount: 1,
startKey: 'foo',
},
@@ -54,7 +54,7 @@ describe('getServerByHostname', () => {
{
name: 'throws an error when the hostname matches multiple servers',
getServersMockedValue: {
agentsList: [server, server],
agents: [server, server],
totalCount: 2,
startKey: 'foo',
},
@@ -63,7 +63,7 @@ describe('getServerByHostname', () => {
{
name: 'returns nothing if the hostname does not match any servers',
getServersMockedValue: {
agentsList: [],
agents: [],
totalCount: 0,
startKey: 'foo',
},
@@ -107,22 +107,22 @@ describe('searchResources', () => {
const tshClient: Partial<tsh.TshdClient> = {
getServers: jest.fn().mockResolvedValueOnce({
agentsList: [server],
agents: [server],
totalCount: 1,
startKey: '',
}),
getDatabases: jest.fn().mockResolvedValueOnce({
agentsList: [db],
agents: [db],
totalCount: 1,
startKey: '',
}),
getKubes: jest.fn().mockResolvedValueOnce({
agentsList: [kube],
agents: [kube],
totalCount: 1,
startKey: '',
}),
getApps: jest.fn().mockResolvedValueOnce({
agentsList: [app],
agents: [app],
totalCount: 1,
startKey: '',
}),
@@ -161,7 +161,7 @@ describe('searchResources', () => {
const server = makeServer();
const tshClient: Partial<tsh.TshdClient> = {
getServers: jest.fn().mockResolvedValueOnce({
agentsList: [server],
agents: [server],
totalCount: 1,
startKey: '',
}),
@@ -38,7 +38,7 @@ export class ResourcesService {
hostname: string
): Promise<types.Server | undefined> {
const query = `name == "${hostname}"`;
const { agentsList: servers } = await this.fetchServers({
const { agents: servers } = await this.fetchServers({
clusterUri,
query,
limit: 2,
@@ -94,7 +94,7 @@ export class ResourcesService {
const getServers = () =>
this.fetchServers(params).then(
res =>
res.agentsList.map(resource => ({
res.agents.map(resource => ({
kind: 'server' as const,
resource,
})),
@@ -104,7 +104,7 @@ export class ResourcesService {
const getApps = () =>
this.fetchApps(params).then(
res =>
res.agentsList.map(resource => ({
res.agents.map(resource => ({
kind: 'app' as const,
resource: makeApp(resource),
})),
@@ -113,7 +113,7 @@ export class ResourcesService {
const getDatabases = () =>
this.fetchDatabases(params).then(
res =>
res.agentsList.map(resource => ({
res.agents.map(resource => ({
kind: 'database' as const,
resource,
})),
@@ -123,7 +123,7 @@ export class ResourcesService {
const getKubes = () =>
this.fetchKubes(params).then(
res =>
res.agentsList.map(resource => ({
res.agents.map(resource => ({
kind: 'kube' as const,
resource,
})),

Some files were not shown because too many files have changed in this diff Show More